get types mostly working

pull/12413/head
Rich Harris 2 years ago
parent 0a350f5c92
commit 4301db72c3

@ -1,21 +1,20 @@
/** @import { Snapshot } from './types' */
import { get_prototype_of, is_array, object_prototype } from './utils.js';
/**
* @template T
* @param {T} value
* @returns {T}
* @returns {Snapshot<T>}
*/
export function snapshot(value) {
return /** @type {T} */ (
clone(/** @type {import('#client').ProxyStateObject} */ (value), new Map())
);
return clone(value, new Map());
}
/**
* @template {import('#client').ProxyStateObject} T
* @template T
* @param {T} value
* @param {Map<T, Record<string | symbol, any>>} cloned
* @returns {Record<string | symbol, any>}
* @param {Map<T, Snapshot<T>>} cloned
* @returns {Snapshot<T>}
*/
function clone(value, cloned) {
if (typeof value === 'object' && value !== null) {
@ -23,8 +22,7 @@ function clone(value, cloned) {
if (unwrapped !== undefined) return unwrapped;
if (is_array(value)) {
/** @type {Record<string | symbol, any>} */
const copy = [];
const copy = /** @type {Snapshot<any>} */ ([]);
cloned.set(value, copy);
for (const element of value) {
@ -35,21 +33,22 @@ function clone(value, cloned) {
}
if (get_prototype_of(value) === object_prototype) {
/** @type {Record<string | symbol, any>} */
/** @type {Snapshot<any>} */
const copy = {};
cloned.set(value, copy);
for (var key in value) {
// @ts-expect-error
copy[key] = clone(value[key], cloned);
}
return copy;
}
if (typeof value.toJSON === 'function') {
return clone(value.toJSON(), cloned);
if (typeof (/** @type {T & { toJSON?: any } } */ (value).toJSON) === 'function') {
return clone(/** @type {T & { toJSON(): any } } */ (value).toJSON(), cloned);
}
}
return structuredClone(value);
return /** @type {Snapshot<T>} */ (structuredClone(value));
}

@ -60,6 +60,7 @@ test('class with state field', () => {
const copy = snapshot(new Foo());
// @ts-expect-error I can't figure out a way to exclude prototype properties
assert.deepEqual(copy, { x: 1 });
});

@ -6,3 +6,72 @@ export type Store<V> = {
export type SourceLocation =
| [line: number, column: number]
| [line: number, column: number, SourceLocation[]];
type Primitive = string | number | boolean | null | undefined;
type TypedArray =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| BigInt64Array
| BigUint64Array;
/** The things that `structuredClone` can handle — https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm */
type Cloneable =
| ArrayBuffer
| DataView
| Date
| Error
| Map<any, any>
| RegExp
| Set<any>
| TypedArray
// web APIs
| Blob
| CryptoKey
| DOMException
| DOMMatrix
| DOMMatrixReadOnly
| DOMPoint
| DOMPointReadOnly
| DOMQuad
| DOMRect
| DOMRectReadOnly
| File
| FileList
| FileSystemDirectoryHandle
| FileSystemFileHandle
| FileSystemHandle
| ImageBitmap
| ImageData
| RTCCertificate
| VideoFrame;
/** Turn `SvelteDate`, `SvelteMap` and `SvelteSet` into their non-reactive counterparts. (`URL` is uncloneable.) */
type NonReactive<T> = T extends Date
? Date
: T extends Map<infer K, infer V>
? Map<K, V>
: T extends Set<infer K>
? Set<K>
: T;
export type Snapshot<T> = T extends Primitive
? T
: T extends Cloneable
? NonReactive<T>
: T extends { toJSON(): infer R }
? R
: T extends Array<infer U>
? Array<Snapshot<U>>
: T extends object
? T extends { [key: string]: any }
? { [K in keyof T]: Snapshot<T[K]> }
: never
: never;

Loading…
Cancel
Save