From 79fef6aa79dad02fb8f450ad6ec7a0d451d439ed Mon Sep 17 00:00:00 2001 From: uhyo <uhyo@uhy.ooo> Date: Sun, 10 Mar 2024 20:58:17 +0900 Subject: [PATCH 1/4] feat: structuredClone typing Co-Authored-By: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> --- lib/lib.dom.d.ts | 86 +++++++++++++++++++++++++++++++++++++ tests/src/dom.ts | 108 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 1 deletion(-) diff --git a/lib/lib.dom.d.ts b/lib/lib.dom.d.ts index 2959d84..d1ee7f7 100644 --- a/lib/lib.dom.d.ts +++ b/lib/lib.dom.d.ts @@ -66,3 +66,89 @@ interface FontFaceSet extends EventTarget { 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<R> = {[k in keyof Weaken]: R extends Weaken[k] ? true : false}; + type SelectNumericLiteral<H> = number extends H ? never : H; + type FilterByNumericLiteralKey<R extends Record<string | number, any>> = {[ + k in keyof R as `${R[k] extends true ? Exclude<SelectNumericLiteral<k>, symbol> : never}` + ]: []}; + type HitWeakenEntry<E> = keyof FilterByNumericLiteralKey<MapSubtype<E>>; + + type NonCloneablePrimitive = Function | { new(...args: any[]): any } | ((...args: any[]) => any) | symbol; + + type Writeable<T> = T extends readonly [] ? [] : T extends readonly [infer X, ...infer XS] ? [X, ...XS] : T extends { [x: PropertyKey]: any } ? { + -readonly [P in keyof T]: Writeable<T[P]>; + } : T; + + type StructuredCloneOutput<T> = + T extends NonCloneablePrimitive + ? never + : T extends ReadonlyArray<any> + ? number extends T["length"] + ? Array<StructuredCloneOutput<T[number]>> + : T extends [infer X, ...infer XS] + ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] + : Writeable<T> + : T extends Map<infer K, infer V> + ? Map<StructuredCloneOutput<K>, StructuredCloneOutput<V>> + : T extends Set<infer E> + ? Set<StructuredCloneOutput<E>> + : T extends Record<any, any> + ? HitWeakenEntry<T> extends never + ? Writeable<{-readonly [k in Exclude<keyof T, symbol> as `${[StructuredCloneOutput<T[k]>] extends [never] ? never : k}`]: StructuredCloneOutput<T[k]>}> + // hit + : Weaken[HitWeakenEntry<T>] + : T + + type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; + + // 上限が不正にきつくなっているのを無視する + type NeverOrUnknown<T> = [T] extends [never] ? never : unknown; + } +} + +/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +declare function structuredClone<const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown<BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T>>>>( + value: T, options?: StructuredSerializeOptions, +): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<T> \ No newline at end of file diff --git a/tests/src/dom.ts b/tests/src/dom.ts index eb13871..0e8221e 100644 --- a/tests/src/dom.ts +++ b/tests/src/dom.ts @@ -1,7 +1,113 @@ -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<JSONValue>(json); }; + +// structuredClone +{ + // primitives + expectType<5>(structuredClone(5)); + expectType<"hello">(structuredClone("hello")); + expectType<true>(structuredClone(true)); + expectType<undefined>(structuredClone(undefined)); + expectType<null>(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<number[]>(structuredClone(arr)); + // read-onlyness is removed + { + const a: readonly number[] = [1, 2, 3]; + const b = structuredClone(a); + expectType<number[]>(b); + b.push(4); + } + // TypedArrays + expectType<Int16Array>(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<Int16Array>(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 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<Map<Int16Array, Int32Array>>(i); + expectNotType<Map<Weirdo, Weirdo2>>(i); + + const j = new Set([new Weirdo()]); + const k: Set<Int16Array> = structuredClone(j); + expectNotType<Set<Weirdo>>(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); + } +} From ab735ba06f1f64dd277e31c70210c232dccd42e4 Mon Sep 17 00:00:00 2001 From: uhyo <uhyo@uhy.ooo> Date: Sun, 10 Mar 2024 21:33:48 +0900 Subject: [PATCH 2/4] fix: fix handling of readonly tuple type --- generated/lib.dom.d.ts | 137 ++++++++++++++++++++++++++++++++++++++++- lib/lib.dom.d.ts | 2 +- tests/src/dom.ts | 5 ++ 3 files changed, 141 insertions(+), 3 deletions(-) diff --git a/generated/lib.dom.d.ts b/generated/lib.dom.d.ts index c45f271..a465388 100644 --- a/generated/lib.dom.d.ts +++ b/generated/lib.dom.d.ts @@ -34081,11 +34081,21 @@ declare function setTimeout( timeout?: number, ...arguments: any[] ): number; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ -declare function structuredClone<T = any>( +declare function structuredClone< + const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< + BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< + BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> + > + >, +>( value: T, options?: StructuredSerializeOptions, -): T; +): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<T>; +// /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +// declare function structuredClone<T = any>(value: T, options?: StructuredSerializeOptions): T; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage) */ declare var sessionStorage: Storage; declare function addEventListener<K extends keyof WindowEventMap>( @@ -34742,3 +34752,126 @@ 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<R> = { + [k in keyof Weaken]: R extends Weaken[k] ? true : false; + }; + type SelectNumericLiteral<H> = number extends H ? never : H; + type FilterByNumericLiteralKey<R extends Record<string | number, any>> = { + [k in keyof R as `${R[k] extends true ? Exclude<SelectNumericLiteral<k>, symbol> : never}`]: []; + }; + type HitWeakenEntry<E> = keyof FilterByNumericLiteralKey<MapSubtype<E>>; + + type NonCloneablePrimitive = + | Function + | { new (...args: any[]): any } + | ((...args: any[]) => any) + | symbol; + + type Writeable<T> = T extends readonly [] + ? [] + : T extends readonly [infer X, ...infer XS] + ? [X, ...XS] + : T extends { [x: PropertyKey]: any } + ? { + -readonly [P in keyof T]: Writeable<T[P]>; + } + : T; + + type StructuredCloneOutput<T> = T extends NonCloneablePrimitive + ? never + : T extends ReadonlyArray<any> + ? number extends T["length"] + ? Array<StructuredCloneOutput<T[number]>> + : T extends readonly [infer X, ...infer XS] + ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] + : Writeable<T> + : T extends Map<infer K, infer V> + ? Map<StructuredCloneOutput<K>, StructuredCloneOutput<V>> + : T extends Set<infer E> + ? Set<StructuredCloneOutput<E>> + : T extends Record<any, any> + ? HitWeakenEntry<T> extends never + ? Writeable<{ + -readonly [k in Exclude< + keyof T, + symbol + > as `${[StructuredCloneOutput<T[k]>] extends [never] ? never : k}`]: StructuredCloneOutput< + T[k] + >; + }> + : // hit + Weaken[HitWeakenEntry<T>] + : T; + + type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; + + // 上限が不正にきつくなっているのを無視する + type NeverOrUnknown<T> = [T] extends [never] ? never : unknown; + } +} diff --git a/lib/lib.dom.d.ts b/lib/lib.dom.d.ts index d1ee7f7..b088db8 100644 --- a/lib/lib.dom.d.ts +++ b/lib/lib.dom.d.ts @@ -127,7 +127,7 @@ declare namespace BetterTypeScriptLibInternals { : T extends ReadonlyArray<any> ? number extends T["length"] ? Array<StructuredCloneOutput<T[number]>> - : T extends [infer X, ...infer XS] + : T extends readonly [infer X, ...infer XS] ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] : Writeable<T> : T extends Map<infer K, infer V> diff --git a/tests/src/dom.ts b/tests/src/dom.ts index 0e8221e..ee98325 100644 --- a/tests/src/dom.ts +++ b/tests/src/dom.ts @@ -57,6 +57,11 @@ const test = async (url: string) => { // @ts-expect-error property does not exist structuredClone(f)[0].weirdo; + const f2: Weirdo[] = [new Weirdo()]; + expectType<Int16Array[]>(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); From 4d8e2a9e17aafb40a4fe5f1504e0e140c7a4c19b Mon Sep 17 00:00:00 2001 From: uhyo <uhyo@uhy.ooo> Date: Sun, 24 Mar 2024 20:19:10 +0900 Subject: [PATCH 3/4] fix --- docs/diff/dom.generated.d.ts.md | 143 +++++++++++++++++++++++++ generated/lib.dom.d.ts | 32 +++--- lib/lib.dom.d.ts | 180 +++++++++++++++++++------------- 3 files changed, 265 insertions(+), 90 deletions(-) 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<T = any>( ++declare function structuredClone< ++ const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< ++ BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< ++ BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> ++ > ++ >, ++>( + value: T, + options?: StructuredSerializeOptions, +-): T; ++): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<T>; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage) */ + declare var sessionStorage: Storage; + declare function addEventListener<K extends keyof WindowEventMap>( + 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<R> = { ++ [k in keyof Weaken]: R extends Weaken[k] ? true : false; ++ }; ++ type SelectNumericLiteral<H> = number extends H ? never : H; ++ type FilterByNumericLiteralKey<R extends Record<string | number, any>> = { ++ [k in keyof R as `${R[k] extends true ? Exclude<SelectNumericLiteral<k>, symbol> : never}`]: []; ++ }; ++ type HitWeakenEntry<E> = keyof FilterByNumericLiteralKey<MapSubtype<E>>; ++ ++ type NonCloneablePrimitive = ++ | Function ++ | { new (...args: any[]): any } ++ | ((...args: any[]) => any) ++ | symbol; ++ ++ type StructuredCloneOutputObject<T> = { ++ -readonly [K in Exclude<keyof T, symbol> as [ ++ StructuredCloneOutput<T[K]>, ++ ] extends [never] ++ ? never ++ : K]: StructuredCloneOutput<T[K]>; ++ }; ++ ++ type StructuredCloneOutput<T> = T extends NonCloneablePrimitive ++ ? never ++ : T extends ReadonlyArray<any> ++ ? number extends T["length"] ++ ? Array<StructuredCloneOutput<T[number]>> ++ : T extends readonly [infer X, ...infer XS] ++ ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] ++ : T extends [] ++ ? [] ++ : StructuredCloneOutputObject<T> ++ : T extends Map<infer K, infer V> ++ ? Map<StructuredCloneOutput<K>, StructuredCloneOutput<V>> ++ : T extends Set<infer E> ++ ? Set<StructuredCloneOutput<E>> ++ : T extends Record<any, any> ++ ? HitWeakenEntry<T> extends never ++ ? StructuredCloneOutputObject<T> ++ : Weaken[HitWeakenEntry<T>] ++ : T; ++ ++ type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; ++ ++ // 上限が不正にきつくなっているのを無視する ++ type NeverOrUnknown<T> = [T] extends [never] ? never : unknown; ++ } ++} ``` diff --git a/generated/lib.dom.d.ts b/generated/lib.dom.d.ts index a465388..93d7c91 100644 --- a/generated/lib.dom.d.ts +++ b/generated/lib.dom.d.ts @@ -34833,15 +34833,13 @@ declare namespace BetterTypeScriptLibInternals { | ((...args: any[]) => any) | symbol; - type Writeable<T> = T extends readonly [] - ? [] - : T extends readonly [infer X, ...infer XS] - ? [X, ...XS] - : T extends { [x: PropertyKey]: any } - ? { - -readonly [P in keyof T]: Writeable<T[P]>; - } - : T; + type StructuredCloneOutputObject<T> = { + -readonly [K in Exclude<keyof T, symbol> as [ + StructuredCloneOutput<T[K]>, + ] extends [never] + ? never + : K]: StructuredCloneOutput<T[K]>; + }; type StructuredCloneOutput<T> = T extends NonCloneablePrimitive ? never @@ -34850,23 +34848,17 @@ declare namespace BetterTypeScriptLibInternals { ? Array<StructuredCloneOutput<T[number]>> : T extends readonly [infer X, ...infer XS] ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] - : Writeable<T> + : T extends [] + ? [] + : StructuredCloneOutputObject<T> : T extends Map<infer K, infer V> ? Map<StructuredCloneOutput<K>, StructuredCloneOutput<V>> : T extends Set<infer E> ? Set<StructuredCloneOutput<E>> : T extends Record<any, any> ? HitWeakenEntry<T> extends never - ? Writeable<{ - -readonly [k in Exclude< - keyof T, - symbol - > as `${[StructuredCloneOutput<T[k]>] extends [never] ? never : k}`]: StructuredCloneOutput< - T[k] - >; - }> - : // hit - Weaken[HitWeakenEntry<T>] + ? StructuredCloneOutputObject<T> + : Weaken[HitWeakenEntry<T>] : T; type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; diff --git a/lib/lib.dom.d.ts b/lib/lib.dom.d.ts index b088db8..d967157 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<This = undefined>( 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<This = undefined>( callbackfn: (this: This, value: unknown, key: string, parent: this) => void, - thisArg?: This + thisArg?: This, ): void; } @@ -61,85 +61,118 @@ 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 Basics = [ + EvalError, + RangeError, + ReferenceError, + TypeError, + SyntaxError, + URIError, + Error, + Boolean, + String, + Date, + RegExp, + ]; type DOMSpecifics = [ - DOMException, - DOMMatrix, - DOMMatrixReadOnly, - DOMPoint, - DOMPointReadOnly, - DOMQuad, - DOMRect, - DOMRectReadOnly, - ] + DOMException, + DOMMatrix, + DOMMatrixReadOnly, + DOMPoint, + DOMPointReadOnly, + DOMQuad, + DOMRect, + DOMRectReadOnly, + ]; type FileSystemTypeFamily = [ - FileSystemDirectoryHandle, - FileSystemFileHandle, - FileSystemHandle, - ] + FileSystemDirectoryHandle, + FileSystemFileHandle, + FileSystemHandle, + ]; type WebGPURelatedTypeFamily = [ - // GPUCompilationInfo, - // GPUCompilationMessage, - ] + // GPUCompilationInfo, + // GPUCompilationMessage, + ]; type TypedArrayFamily = [ - Int8Array, Int16Array, Int32Array, BigInt64Array, Uint8Array, Uint16Array, Uint32Array, BigUint64Array, Uint8ClampedArray, - ] + 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, + ...Basics, + // AudioData, + Blob, + // CropTarget, + // CryptoTarget, + ...DOMSpecifics, + ...FileSystemTypeFamily, + ...WebGPURelatedTypeFamily, + File, + FileList, + ...TypedArrayFamily, + DataView, + ImageBitmap, + ImageData, + RTCCertificate, + VideoFrame, ]; - type MapSubtype<R> = {[k in keyof Weaken]: R extends Weaken[k] ? true : false}; + type MapSubtype<R> = { + [k in keyof Weaken]: R extends Weaken[k] ? true : false; + }; type SelectNumericLiteral<H> = number extends H ? never : H; - type FilterByNumericLiteralKey<R extends Record<string | number, any>> = {[ - k in keyof R as `${R[k] extends true ? Exclude<SelectNumericLiteral<k>, symbol> : never}` - ]: []}; + type FilterByNumericLiteralKey<R extends Record<string | number, any>> = { + [k in keyof R as `${R[k] extends true ? Exclude<SelectNumericLiteral<k>, symbol> : never}`]: []; + }; type HitWeakenEntry<E> = keyof FilterByNumericLiteralKey<MapSubtype<E>>; - type NonCloneablePrimitive = Function | { new(...args: any[]): any } | ((...args: any[]) => any) | symbol; + type NonCloneablePrimitive = + | Function + | { new (...args: any[]): any } + | ((...args: any[]) => any) + | symbol; - type Writeable<T> = T extends readonly [] ? [] : T extends readonly [infer X, ...infer XS] ? [X, ...XS] : T extends { [x: PropertyKey]: any } ? { - -readonly [P in keyof T]: Writeable<T[P]>; - } : T; + type StructuredCloneOutputObject<T> = { + -readonly [K in Exclude<keyof T, symbol> as [ + StructuredCloneOutput<T[K]>, + ] extends [never] + ? never + : K]: StructuredCloneOutput<T[K]>; + }; - type StructuredCloneOutput<T> = - T extends NonCloneablePrimitive - ? never - : T extends ReadonlyArray<any> - ? number extends T["length"] - ? Array<StructuredCloneOutput<T[number]>> - : T extends readonly [infer X, ...infer XS] - ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] - : Writeable<T> - : T extends Map<infer K, infer V> - ? Map<StructuredCloneOutput<K>, StructuredCloneOutput<V>> - : T extends Set<infer E> + type StructuredCloneOutput<T> = T extends NonCloneablePrimitive + ? never + : T extends ReadonlyArray<any> + ? number extends T["length"] + ? Array<StructuredCloneOutput<T[number]>> + : T extends readonly [infer X, ...infer XS] + ? [StructuredCloneOutput<X>, ...StructuredCloneOutput<XS>] + : T extends [] + ? [] + : StructuredCloneOutputObject<T> + : T extends Map<infer K, infer V> + ? Map<StructuredCloneOutput<K>, StructuredCloneOutput<V>> + : T extends Set<infer E> ? Set<StructuredCloneOutput<E>> : T extends Record<any, any> - ? HitWeakenEntry<T> extends never - ? Writeable<{-readonly [k in Exclude<keyof T, symbol> as `${[StructuredCloneOutput<T[k]>] extends [never] ? never : k}`]: StructuredCloneOutput<T[k]>}> - // hit - : Weaken[HitWeakenEntry<T>] - : T + ? HitWeakenEntry<T> extends never + ? StructuredCloneOutputObject<T> + : Weaken[HitWeakenEntry<T>] + : T; type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; @@ -149,6 +182,13 @@ declare namespace BetterTypeScriptLibInternals { } /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ -declare function structuredClone<const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown<BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T>>>>( - value: T, options?: StructuredSerializeOptions, -): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<T> \ No newline at end of file +declare function structuredClone< + const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< + BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< + BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> + > + >, +>( + value: T, + options?: StructuredSerializeOptions, +): BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput<T>; From ee94fbee329feaa41a56dd2d4b83131f78bfc236 Mon Sep 17 00:00:00 2001 From: uhyo <uhyo@uhy.ooo> Date: Sun, 24 Mar 2024 20:28:44 +0900 Subject: [PATCH 4/4] export easily nameable constraint --- generated/lib.dom.d.ts | 14 ++++++++------ lib/lib.dom.d.ts | 14 ++++++++------ tests/src/dom.ts | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/generated/lib.dom.d.ts b/generated/lib.dom.d.ts index 93d7c91..8a877e7 100644 --- a/generated/lib.dom.d.ts +++ b/generated/lib.dom.d.ts @@ -34084,11 +34084,7 @@ declare function setTimeout( /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ declare function structuredClone< - const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< - BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< - BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> - > - >, + const T extends BetterTypeScriptLibInternals.StructuredClone.Constraint<T>, >( value: T, options?: StructuredSerializeOptions, @@ -34863,7 +34859,13 @@ declare namespace BetterTypeScriptLibInternals { type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; - // 上限が不正にきつくなっているのを無視する type NeverOrUnknown<T> = [T] extends [never] ? never : unknown; + + export type Constraint<T> = + BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< + BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< + BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> + > + >; } } diff --git a/lib/lib.dom.d.ts b/lib/lib.dom.d.ts index d967157..a3ce0d3 100644 --- a/lib/lib.dom.d.ts +++ b/lib/lib.dom.d.ts @@ -176,18 +176,20 @@ declare namespace BetterTypeScriptLibInternals { type AvoidCyclicConstraint<T> = [T] extends [infer R] ? R : never; - // 上限が不正にきつくなっているのを無視する type NeverOrUnknown<T> = [T] extends [never] ? never : unknown; + + export type Constraint<T> = + BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< + BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< + BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> + > + >; } } /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ declare function structuredClone< - const T extends BetterTypeScriptLibInternals.StructuredClone.NeverOrUnknown< - BetterTypeScriptLibInternals.StructuredClone.StructuredCloneOutput< - BetterTypeScriptLibInternals.StructuredClone.AvoidCyclicConstraint<T> - > - >, + const T extends BetterTypeScriptLibInternals.StructuredClone.Constraint<T>, >( value: T, options?: StructuredSerializeOptions, diff --git a/tests/src/dom.ts b/tests/src/dom.ts index ee98325..59f4d12 100644 --- a/tests/src/dom.ts +++ b/tests/src/dom.ts @@ -115,4 +115,25 @@ const test = async (url: string) => { // @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<T>, + >(obj: T) { + return structuredClone(obj); + } + } }