diff --git a/docs/diff/dom.generated.d.ts.md b/docs/diff/dom.generated.d.ts.md index 14a40a6..184e1a8 100644 --- a/docs/diff/dom.generated.d.ts.md +++ b/docs/diff/dom.generated.d.ts.md @@ -126,5 +126,148 @@ Index: dom.generated.d.ts } declare var RTCStatsReport: { +@@ -34051,13 +34071,20 @@ + handler: TimerHandler, + timeout?: number, + ...arguments: any[] + ): number; ++ + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +-declare function structuredClone( ++declare function structuredClone< ++ const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< ++ BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< ++ BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint ++ > ++ >, ++>( + value: T, + options?: StructuredSerializeOptions, +-): T; ++): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage) */ + declare var sessionStorage: Storage; + declare function addEventListener( + type: K, +@@ -34712,4 +34739,119 @@ + | "blob" + | "document" + | "json" + | "text"; ++// -------------------- ++ ++declare namespace BetterTypeScriptLibInternals { ++ export namespace StructuredClone { ++ type Basics = [ ++ EvalError, ++ RangeError, ++ ReferenceError, ++ TypeError, ++ SyntaxError, ++ URIError, ++ Error, ++ Boolean, ++ String, ++ Date, ++ RegExp, ++ ]; ++ type DOMSpecifics = [ ++ DOMException, ++ DOMMatrix, ++ DOMMatrixReadOnly, ++ DOMPoint, ++ DOMPointReadOnly, ++ DOMQuad, ++ DOMRect, ++ DOMRectReadOnly, ++ ]; ++ type FileSystemTypeFamily = [ ++ FileSystemDirectoryHandle, ++ FileSystemFileHandle, ++ FileSystemHandle, ++ ]; ++ type WebGPURelatedTypeFamily = [ ++ // GPUCompilationInfo, ++ // GPUCompilationMessage, ++ ]; ++ type TypedArrayFamily = [ ++ Int8Array, ++ Int16Array, ++ Int32Array, ++ BigInt64Array, ++ Uint8Array, ++ Uint16Array, ++ Uint32Array, ++ BigUint64Array, ++ Uint8ClampedArray, ++ ]; ++ type Weaken = [ ++ ...Basics, ++ // AudioData, ++ Blob, ++ // CropTarget, ++ // CryptoTarget, ++ ...DOMSpecifics, ++ ...FileSystemTypeFamily, ++ ...WebGPURelatedTypeFamily, ++ File, ++ FileList, ++ ...TypedArrayFamily, ++ DataView, ++ ImageBitmap, ++ ImageData, ++ RTCCertificate, ++ VideoFrame, ++ ]; ++ ++ type MapSubtype = { ++ [k in keyof Weaken]: R extends Weaken[k] ? true : false; ++ }; ++ type SelectNumericLiteral = number extends H ? never : H; ++ type FilterByNumericLiteralKey> = { ++ [k in keyof R as `${R[k] extends true ? Exclude, symbol> : never}`]: []; ++ }; ++ type HitWeakenEntry = keyof FilterByNumericLiteralKey>; ++ ++ type NonCloneablePrimitive = ++ | Function ++ | { new (...args: any[]): any } ++ | ((...args: any[]) => any) ++ | symbol; ++ ++ type StructuredCloneOutputObject = { ++ -readonly [K in Exclude as [ ++ StructuredCloneOutput, ++ ] extends [never] ++ ? never ++ : K]: StructuredCloneOutput; ++ }; ++ ++ type StructuredCloneOutput = T extends NonCloneablePrimitive ++ ? never ++ : T extends ReadonlyArray ++ ? number extends T["length"] ++ ? Array> ++ : T extends readonly [infer X, ...infer XS] ++ ? [StructuredCloneOutput, ...StructuredCloneOutput] ++ : T extends [] ++ ? [] ++ : StructuredCloneOutputObject ++ : T extends Map ++ ? Map, StructuredCloneOutput> ++ : T extends Set ++ ? Set> ++ : T extends Record ++ ? HitWeakenEntry extends never ++ ? StructuredCloneOutputObject ++ : Weaken[HitWeakenEntry] ++ : T; ++ ++ type AvoidCyclicConstraint = [T] extends [infer R] ? R : never; ++ ++ // 上限が不正にきつくなっているのを無視する ++ type NeverOrUnknown = [T] extends [never] ? never : unknown; ++ } ++} ``` diff --git a/generated/lib.dom.d.ts b/generated/lib.dom.d.ts index c45f271..8a877e7 100644 --- a/generated/lib.dom.d.ts +++ b/generated/lib.dom.d.ts @@ -34081,11 +34081,17 @@ declare function setTimeout( timeout?: number, ...arguments: any[] ): number; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ -declare function structuredClone( +declare function structuredClone< + const T extends BetterTypeScriptLibInternals.StructuredClone.Constraint, +>( value: T, options?: StructuredSerializeOptions, -): T; +): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput; +// /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +// declare function structuredClone(value: T, options?: StructuredSerializeOptions): T; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage) */ declare var sessionStorage: Storage; declare function addEventListener( @@ -34742,3 +34748,124 @@ type XMLHttpRequestResponseType = | "document" | "json" | "text"; +// -------------------- + +declare namespace BetterTypeScriptLibInternals { + export namespace StructuredClone { + type Basics = [ + EvalError, + RangeError, + ReferenceError, + TypeError, + SyntaxError, + URIError, + Error, + Boolean, + String, + Date, + RegExp, + ]; + type DOMSpecifics = [ + DOMException, + DOMMatrix, + DOMMatrixReadOnly, + DOMPoint, + DOMPointReadOnly, + DOMQuad, + DOMRect, + DOMRectReadOnly, + ]; + type FileSystemTypeFamily = [ + FileSystemDirectoryHandle, + FileSystemFileHandle, + FileSystemHandle, + ]; + type WebGPURelatedTypeFamily = [ + // GPUCompilationInfo, + // GPUCompilationMessage, + ]; + type TypedArrayFamily = [ + Int8Array, + Int16Array, + Int32Array, + BigInt64Array, + Uint8Array, + Uint16Array, + Uint32Array, + BigUint64Array, + Uint8ClampedArray, + ]; + type Weaken = [ + ...Basics, + // AudioData, + Blob, + // CropTarget, + // CryptoTarget, + ...DOMSpecifics, + ...FileSystemTypeFamily, + ...WebGPURelatedTypeFamily, + File, + FileList, + ...TypedArrayFamily, + DataView, + ImageBitmap, + ImageData, + RTCCertificate, + VideoFrame, + ]; + + type MapSubtype = { + [k in keyof Weaken]: R extends Weaken[k] ? true : false; + }; + type SelectNumericLiteral = number extends H ? never : H; + type FilterByNumericLiteralKey> = { + [k in keyof R as `${R[k] extends true ? Exclude, symbol> : never}`]: []; + }; + type HitWeakenEntry = keyof FilterByNumericLiteralKey>; + + type NonCloneablePrimitive = + | Function + | { new (...args: any[]): any } + | ((...args: any[]) => any) + | symbol; + + type StructuredCloneOutputObject = { + -readonly [K in Exclude as [ + StructuredCloneOutput, + ] extends [never] + ? never + : K]: StructuredCloneOutput; + }; + + type StructuredCloneOutput = T extends NonCloneablePrimitive + ? never + : T extends ReadonlyArray + ? number extends T["length"] + ? Array> + : T extends readonly [infer X, ...infer XS] + ? [StructuredCloneOutput, ...StructuredCloneOutput] + : T extends [] + ? [] + : StructuredCloneOutputObject + : T extends Map + ? Map, StructuredCloneOutput> + : T extends Set + ? Set> + : T extends Record + ? HitWeakenEntry extends never + ? StructuredCloneOutputObject + : Weaken[HitWeakenEntry] + : T; + + type AvoidCyclicConstraint = [T] extends [infer R] ? R : never; + + type NeverOrUnknown = [T] extends [never] ? never : unknown; + + export type Constraint = + BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< + BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< + BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint + > + >; + } +} diff --git a/lib/lib.dom.d.ts b/lib/lib.dom.d.ts index 2959d84..a3ce0d3 100644 --- a/lib/lib.dom.d.ts +++ b/lib/lib.dom.d.ts @@ -9,16 +9,16 @@ interface AudioParamMap { this: This, value: AudioParam, key: string, - parent: this + parent: this, ) => void, - thisArg?: This + thisArg?: This, ): void; } interface EventCounts { forEach( callbackfn: (this: This, value: number, key: string, parent: this) => void, - thisArg?: This + thisArg?: This, ): void; } @@ -29,9 +29,9 @@ interface MIDIInputMap { this: This, value: MIDIInput, key: string, - parent: this + parent: this, ) => void, - thisArg?: This + thisArg?: This, ): void; } @@ -42,16 +42,16 @@ interface MIDIOutputMap { this: This, value: MIDIOutput, key: string, - parent: this + parent: this, ) => void, - thisArg?: This + thisArg?: This, ): void; } interface RTCStatsReport { forEach( callbackfn: (this: This, value: unknown, key: string, parent: this) => void, - thisArg?: This + thisArg?: This, ): void; } @@ -61,8 +61,136 @@ interface FontFaceSet extends EventTarget { this: This, value: FontFace, key: FontFace, - parent: this + parent: this, ) => void, - thisArg?: This + thisArg?: This, ): void; } + +declare namespace BetterTypeScriptLibInternals { + export namespace StructuredClone { + type Basics = [ + EvalError, + RangeError, + ReferenceError, + TypeError, + SyntaxError, + URIError, + Error, + Boolean, + String, + Date, + RegExp, + ]; + type DOMSpecifics = [ + DOMException, + DOMMatrix, + DOMMatrixReadOnly, + DOMPoint, + DOMPointReadOnly, + DOMQuad, + DOMRect, + DOMRectReadOnly, + ]; + type FileSystemTypeFamily = [ + FileSystemDirectoryHandle, + FileSystemFileHandle, + FileSystemHandle, + ]; + type WebGPURelatedTypeFamily = [ + // GPUCompilationInfo, + // GPUCompilationMessage, + ]; + type TypedArrayFamily = [ + Int8Array, + Int16Array, + Int32Array, + BigInt64Array, + Uint8Array, + Uint16Array, + Uint32Array, + BigUint64Array, + Uint8ClampedArray, + ]; + type Weaken = [ + ...Basics, + // AudioData, + Blob, + // CropTarget, + // CryptoTarget, + ...DOMSpecifics, + ...FileSystemTypeFamily, + ...WebGPURelatedTypeFamily, + File, + FileList, + ...TypedArrayFamily, + DataView, + ImageBitmap, + ImageData, + RTCCertificate, + VideoFrame, + ]; + + type MapSubtype = { + [k in keyof Weaken]: R extends Weaken[k] ? true : false; + }; + type SelectNumericLiteral = number extends H ? never : H; + type FilterByNumericLiteralKey> = { + [k in keyof R as `${R[k] extends true ? Exclude, symbol> : never}`]: []; + }; + type HitWeakenEntry = keyof FilterByNumericLiteralKey>; + + type NonCloneablePrimitive = + | Function + | { new (...args: any[]): any } + | ((...args: any[]) => any) + | symbol; + + type StructuredCloneOutputObject = { + -readonly [K in Exclude as [ + StructuredCloneOutput, + ] extends [never] + ? never + : K]: StructuredCloneOutput; + }; + + type StructuredCloneOutput = T extends NonCloneablePrimitive + ? never + : T extends ReadonlyArray + ? number extends T["length"] + ? Array> + : T extends readonly [infer X, ...infer XS] + ? [StructuredCloneOutput, ...StructuredCloneOutput] + : T extends [] + ? [] + : StructuredCloneOutputObject + : T extends Map + ? Map, StructuredCloneOutput> + : T extends Set + ? Set> + : T extends Record + ? HitWeakenEntry extends never + ? StructuredCloneOutputObject + : Weaken[HitWeakenEntry] + : T; + + type AvoidCyclicConstraint = [T] extends [infer R] ? R : never; + + type NeverOrUnknown = [T] extends [never] ? never : unknown; + + export type Constraint = + BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< + BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< + BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint + > + >; + } +} + +/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +declare function structuredClone< + const T extends BetterTypeScriptLibInternals.StructuredClone.Constraint, +>( + value: T, + options?: StructuredSerializeOptions, +): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput; diff --git a/tests/src/dom.ts b/tests/src/dom.ts index eb13871..59f4d12 100644 --- a/tests/src/dom.ts +++ b/tests/src/dom.ts @@ -1,7 +1,139 @@ -import { expectType } from "tsd"; +import { expectNotType, expectType } from "tsd"; const test = async (url: string) => { const response = await fetch(url); const json = await response.json(); expectType(json); }; + +// structuredClone +{ + // primitives + expectType<5>(structuredClone(5)); + expectType<"hello">(structuredClone("hello")); + expectType(structuredClone(true)); + expectType(structuredClone(undefined)); + expectType(structuredClone(null)); + // plain objects + expectType<{ a: 5 }>(structuredClone({ a: 5 })); + expectType<{ + a: 5; + nested: { + b: "hello"; + }; + }>(structuredClone({ a: 5, nested: { b: "hello" } })); + const obj = { a: 5 }; + expectType<{ a: number }>(structuredClone(obj)); + // arrays + expectType<[1, 2, 3]>(structuredClone([1, 2, 3])); + expectType<["a", "b", "c"]>(structuredClone(["a", "b", "c"])); + const arr = [1, 2, 3]; + expectType(structuredClone(arr)); + // read-onlyness is removed + { + const a: readonly number[] = [1, 2, 3]; + const b = structuredClone(a); + expectType(b); + b.push(4); + } + // TypedArrays + expectType(structuredClone(new Int16Array())); + { + // class instances are converted to base built-in types + class Weirdo extends Int16Array { + public weirdo: undefined = undefined; + } + + class Weirdo2 extends Int32Array { + public weirdo2: undefined = undefined; + } + + expectType(structuredClone(new Weirdo())); + + // @ts-expect-error property does not exist + structuredClone(new Weirdo()).weirdo; + const f: readonly [Weirdo] = [new Weirdo()] as const; + expectType<[Int16Array]>(structuredClone(f)); + // @ts-expect-error property does not exist + structuredClone(f)[0].weirdo; + + const f2: Weirdo[] = [new Weirdo()]; + expectType(structuredClone(f2)); + // @ts-expect-error property does not exist + structuredClone(f2)[0].weirdo; + + const g = { a: new Weirdo() }; + const g2 = structuredClone(g); + expectType<{ a: Int16Array }>(g2); + // @ts-expect-error property does not exist + g2.a.weirdo; + + const h = new Map([[new Weirdo(), new Weirdo2()]]); + const i = structuredClone(h); + expectType>(i); + expectNotType>(i); + + const j = new Set([new Weirdo()]); + const k: Set = structuredClone(j); + expectNotType>(k); + + class Empty {} + expectType<{}>(structuredClone(new Empty())); + class SingleProp { + hello: number = 3; + } + expectType<{ hello: number }>(structuredClone(new SingleProp())); + + class WithConstructor { + hi: number; + constructor(hi: number) { + this.hi = hi; + } + } + + expectType<{ hi: number }>(structuredClone(new WithConstructor(1))); + + class WithFunction { + hello(): "hi" { + return "hi"; + } + } + + expectType<{}>(structuredClone(new WithFunction())); + // @ts-expect-error + structuredClone(new WithFunction()).hello(); + // ^? + const x = structuredClone({ s: () => 1 }); + // ^? + } + // non-clonable objects + { + // @ts-expect-error + const m = structuredClone(class {}); + // @ts-expect-error + const n = structuredClone(Symbol.iterator); + // @ts-expect-error + const p = structuredClone(() => 1); + } + // unions + { + function getData() { + if (Math.random() > 0.5) { + return { a: 5, b: null }; + } else { + return { a: null, b: "hello" }; + } + } + expectType<{ a: number; b: null } | { a: null; b: string }>( + structuredClone(getData()), + ); + } + // generic functions + { + function func1< + T extends BetterTypeScriptLibInternals.StructuredClone.Constraint, + >(obj: T) { + return structuredClone(obj); + } + } +}