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);
+    }
+  }
 }