From ed6fa8824cba9853b717ca2573995d6ddf6dda4c Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 08:51:06 +0000 Subject: [PATCH 1/7] [ReadonlyArray] `.at()` fix `undefined` issue --- src/entrypoints/array-at.d.ts | 14 +++++++++----- src/entrypoints/utils.d.ts | 9 ++++++++- src/tests/array-at.ts | 6 ++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/entrypoints/array-at.d.ts b/src/entrypoints/array-at.d.ts index 7ab95a2..f4a0dd7 100644 --- a/src/entrypoints/array-at.d.ts +++ b/src/entrypoints/array-at.d.ts @@ -1,9 +1,13 @@ /// interface ReadonlyArray { - at (index: I): `${I}` extends `-${infer J extends number}` - ? `${TSReset.Subtract}` extends `-${number}` - ? undefined - : this[TSReset.Subtract] - : this[I] + at( + index: I + ): TSReset.Equal extends true + ? T | undefined + : `${I}` extends `-${infer J extends number}` + ? `${TSReset.Subtract}` extends `-${number}` + ? undefined + : this[TSReset.Subtract] + : this[I]; } \ No newline at end of file diff --git a/src/entrypoints/utils.d.ts b/src/entrypoints/utils.d.ts index 9d69f0b..a9ad320 100644 --- a/src/entrypoints/utils.d.ts +++ b/src/entrypoints/utils.d.ts @@ -22,8 +22,15 @@ declare namespace TSReset { type BuildTuple = T extends { length: L } ? T : BuildTuple - + type Subtract = BuildTuple extends [...(infer U), ...BuildTuple] ? Length : never + + type Equal = (() => T extends X ? 1 : 2) extends < + T, + >() => T extends Y ? 1 : 2 + ? true + : false; + type NotEqual = true extends Equal ? false : true; } diff --git a/src/tests/array-at.ts b/src/tests/array-at.ts index a2530d4..c02586c 100644 --- a/src/tests/array-at.ts +++ b/src/tests/array-at.ts @@ -32,11 +32,9 @@ doNotExecute(async () => { doNotExecute(async () => { const arr = [false, 1, '2'] as const; - const index: number = 1 + const index = 1 as number const a = arr.at(index) - // WARN: with `"strictNullChecks": true,` the correct type here should include `undefined` - // but the current implementation does not type this as including `undefined` type tests = [ - Expect>, + Expect>, ] }); \ No newline at end of file From c21f78c2fa97586f3d7f50540ae9d06e212c40fe Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:08:35 +0000 Subject: [PATCH 2/7] Fixed Subtract utils type, now works with unions --- src/entrypoints/utils.d.ts | 52 ++++++++++++++++---------------------- src/tests/array-at.ts | 26 +++++++++++++++++++ 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/entrypoints/utils.d.ts b/src/entrypoints/utils.d.ts index a9ad320..91438e1 100644 --- a/src/entrypoints/utils.d.ts +++ b/src/entrypoints/utils.d.ts @@ -1,36 +1,28 @@ declare namespace TSReset { - type NonFalsy = T extends false | 0 | "" | null | undefined | 0n - ? never - : T; + type NonFalsy = T extends false | 0 | "" | null | undefined | 0n ? never : T - type WidenLiteral = T extends string - ? string - : T extends number - ? number - : T extends boolean - ? boolean - : T extends bigint - ? bigint - : T extends symbol - ? symbol - : T; + type WidenLiteral = T extends string + ? string + : T extends number + ? number + : T extends boolean + ? boolean + : T extends bigint + ? bigint + : T extends symbol + ? symbol + : T - type Length = T extends { length: infer L } - ? L - : never + type BuildTuple = T extends { length: L } ? T : BuildTuple - type BuildTuple = T extends { length: L } - ? T - : BuildTuple + type Subtract = A extends number + ? B extends number + ? BuildTuple extends [...infer U, ...BuildTuple] + ? U["length"] + : never + : never + : never - type Subtract = BuildTuple extends [...(infer U), ...BuildTuple] - ? Length - : never - - type Equal = (() => T extends X ? 1 : 2) extends < - T, - >() => T extends Y ? 1 : 2 - ? true - : false; - type NotEqual = true extends Equal ? false : true; + type Equal = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false + type NotEqual = true extends Equal ? false : true } diff --git a/src/tests/array-at.ts b/src/tests/array-at.ts index c02586c..d202ab0 100644 --- a/src/tests/array-at.ts +++ b/src/tests/array-at.ts @@ -30,6 +30,32 @@ doNotExecute(async () => { ] }); +doNotExecute(async () => { + const arr = [false, 1, '2'] as const; + + function index() { + return Math.random() > 0.5 ? 0 : 1 + } + + const a = arr.at(index()) + type tests = [ + Expect>, + ] +}); + +doNotExecute(async () => { + const arr = [false, true, 1, '2'] as const; + + function index() { + return Math.random() > 0.5 ? -1 : -2 + } + + const a = arr.at(index()) + type tests = [ + Expect>, + ] +}); + doNotExecute(async () => { const arr = [false, 1, '2'] as const; const index = 1 as number From 8c520d0821c55167cadfdacad5d2c72f3395c464 Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:11:05 +0000 Subject: [PATCH 3/7] prettier format --- src/entrypoints/array-at.d.ts | 20 ++++++------ src/entrypoints/utils.d.ts | 54 ++++++++++++++++++------------- src/tests/array-at.ts | 60 ++++++++++++++++------------------- 3 files changed, 69 insertions(+), 65 deletions(-) diff --git a/src/entrypoints/array-at.d.ts b/src/entrypoints/array-at.d.ts index f4a0dd7..4db6d9a 100644 --- a/src/entrypoints/array-at.d.ts +++ b/src/entrypoints/array-at.d.ts @@ -1,13 +1,13 @@ /// interface ReadonlyArray { - at( - index: I - ): TSReset.Equal extends true - ? T | undefined - : `${I}` extends `-${infer J extends number}` - ? `${TSReset.Subtract}` extends `-${number}` - ? undefined - : this[TSReset.Subtract] - : this[I]; -} \ No newline at end of file + at( + index: I, + ): TSReset.Equal extends true + ? T | undefined + : `${I}` extends `-${infer J extends number}` + ? `${TSReset.Subtract}` extends `-${number}` + ? undefined + : this[TSReset.Subtract] + : this[I]; +} diff --git a/src/entrypoints/utils.d.ts b/src/entrypoints/utils.d.ts index 91438e1..bca143e 100644 --- a/src/entrypoints/utils.d.ts +++ b/src/entrypoints/utils.d.ts @@ -1,28 +1,38 @@ declare namespace TSReset { - type NonFalsy = T extends false | 0 | "" | null | undefined | 0n ? never : T + type NonFalsy = T extends false | 0 | "" | null | undefined | 0n + ? never + : T; - type WidenLiteral = T extends string - ? string - : T extends number - ? number - : T extends boolean - ? boolean - : T extends bigint - ? bigint - : T extends symbol - ? symbol - : T + type WidenLiteral = T extends string + ? string + : T extends number + ? number + : T extends boolean + ? boolean + : T extends bigint + ? bigint + : T extends symbol + ? symbol + : T; - type BuildTuple = T extends { length: L } ? T : BuildTuple + type BuildTuple = T extends { + length: L; + } + ? T + : BuildTuple; - type Subtract = A extends number - ? B extends number - ? BuildTuple extends [...infer U, ...BuildTuple] - ? U["length"] - : never - : never - : never + type Subtract = A extends number + ? B extends number + ? BuildTuple extends [...infer U, ...BuildTuple] + ? U["length"] + : never + : never + : never; - type Equal = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false - type NotEqual = true extends Equal ? false : true + type Equal = (() => T extends X ? 1 : 2) extends () => T extends Y + ? 1 + : 2 + ? true + : false; + type NotEqual = true extends Equal ? false : true; } diff --git a/src/tests/array-at.ts b/src/tests/array-at.ts index d202ab0..0bdd48d 100644 --- a/src/tests/array-at.ts +++ b/src/tests/array-at.ts @@ -1,66 +1,60 @@ import { doNotExecute, Equal, Expect } from "./utils"; doNotExecute(async () => { - const arr = [false, 1, '2'] as const; + const arr = [false, 1, "2"] as const; - const a = arr.at(0) - const b = arr.at(1) - const c = arr.at(2) - const d = arr.at(3) + const a = arr.at(0); + const b = arr.at(1); + const c = arr.at(2); + const d = arr.at(3); type tests = [ Expect>, Expect>, - Expect>, + Expect>, Expect>, - ] + ]; }); doNotExecute(async () => { - const arr = [false, 1, '2'] as const; + const arr = [false, 1, "2"] as const; - const a = arr.at(-1) - const b = arr.at(-2) - const c = arr.at(-3) - const d = arr.at(-4) + const a = arr.at(-1); + const b = arr.at(-2); + const c = arr.at(-3); + const d = arr.at(-4); type tests = [ - Expect>, + Expect>, Expect>, Expect>, Expect>, - ] + ]; }); doNotExecute(async () => { - const arr = [false, 1, '2'] as const; + const arr = [false, 1, "2"] as const; function index() { - return Math.random() > 0.5 ? 0 : 1 + return Math.random() > 0.5 ? 0 : 1; } - const a = arr.at(index()) - type tests = [ - Expect>, - ] + const a = arr.at(index()); + type tests = [Expect>]; }); doNotExecute(async () => { - const arr = [false, true, 1, '2'] as const; + const arr = [false, true, 1, "2"] as const; function index() { - return Math.random() > 0.5 ? -1 : -2 + return Math.random() > 0.5 ? -1 : -2; } - const a = arr.at(index()) - type tests = [ - Expect>, - ] + const a = arr.at(index()); + type tests = [Expect>]; }); doNotExecute(async () => { - const arr = [false, 1, '2'] as const; - const index = 1 as number - const a = arr.at(index) - type tests = [ - Expect>, - ] -}); \ No newline at end of file + const arr = [false, 1, "2"] as const; + const index = 1 as number; + const a = arr.at(index); + type tests = [Expect>]; +}); From 06ac7ee5eb44e918689977a4a901803768b89730 Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:17:01 +0000 Subject: [PATCH 4/7] Added comment explaning why Subtract is the way its --- src/entrypoints/utils.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entrypoints/utils.d.ts b/src/entrypoints/utils.d.ts index bca143e..6ec23aa 100644 --- a/src/entrypoints/utils.d.ts +++ b/src/entrypoints/utils.d.ts @@ -21,6 +21,7 @@ declare namespace TSReset { ? T : BuildTuple; + // Extra `A extends number` and `B extends number` needed for union types to work Such as Subtract<10 | 20, 1> type Subtract = A extends number ? B extends number ? BuildTuple extends [...infer U, ...BuildTuple] From 95fef7a51cc701a9623b4b669a9194f03a701689 Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:47:15 +0000 Subject: [PATCH 5/7] [ReadonlyArray] `.at()` handled floats --- src/entrypoints/array-at.d.ts | 17 ++++++++++++----- src/tests/array-at.ts | 4 ++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/entrypoints/array-at.d.ts b/src/entrypoints/array-at.d.ts index 4db6d9a..3dda71d 100644 --- a/src/entrypoints/array-at.d.ts +++ b/src/entrypoints/array-at.d.ts @@ -1,13 +1,20 @@ /// interface ReadonlyArray { - at( - index: I, + at< + const N extends number, + I extends number = `${N}` extends `${infer J extends number}.${number}` + ? J + : N, + >( + index: N, ): TSReset.Equal extends true ? T | undefined : `${I}` extends `-${infer J extends number}` - ? `${TSReset.Subtract}` extends `-${number}` - ? undefined - : this[TSReset.Subtract] + ? TSReset.Subtract extends infer K extends number + ? [K] extends [never] + ? undefined + : this[K] + : undefined : this[I]; } diff --git a/src/tests/array-at.ts b/src/tests/array-at.ts index 0bdd48d..9af5667 100644 --- a/src/tests/array-at.ts +++ b/src/tests/array-at.ts @@ -7,11 +7,13 @@ doNotExecute(async () => { const b = arr.at(1); const c = arr.at(2); const d = arr.at(3); + const e = arr.at(1.5); type tests = [ Expect>, Expect>, Expect>, Expect>, + Expect>, ]; }); @@ -22,11 +24,13 @@ doNotExecute(async () => { const b = arr.at(-2); const c = arr.at(-3); const d = arr.at(-4); + const e = arr.at(-1.5); type tests = [ Expect>, Expect>, Expect>, Expect>, + Expect>, ]; }); From ab41585e0155b997ebd3d2de703d5b1e087e3baf Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:54:05 +0000 Subject: [PATCH 6/7] [ReadonlyArray] `.at()` Reverted some changes --- src/entrypoints/array-at.d.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/entrypoints/array-at.d.ts b/src/entrypoints/array-at.d.ts index 3dda71d..ae1afb8 100644 --- a/src/entrypoints/array-at.d.ts +++ b/src/entrypoints/array-at.d.ts @@ -11,10 +11,8 @@ interface ReadonlyArray { ): TSReset.Equal extends true ? T | undefined : `${I}` extends `-${infer J extends number}` - ? TSReset.Subtract extends infer K extends number - ? [K] extends [never] - ? undefined - : this[K] - : undefined + ? `${TSReset.Subtract}` extends `-${number}` + ? undefined + : this[TSReset.Subtract] : this[I]; } From 064d896e71f3aa059a783a0f1a0a34d96474dda2 Mon Sep 17 00:00:00 2001 From: Shiba <44804845+DeepDoge@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:56:47 +0000 Subject: [PATCH 7/7] [ReadonlyArray] `.at()` test refactor --- src/tests/array-at.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/tests/array-at.ts b/src/tests/array-at.ts index 9af5667..22a5d10 100644 --- a/src/tests/array-at.ts +++ b/src/tests/array-at.ts @@ -37,22 +37,18 @@ doNotExecute(async () => { doNotExecute(async () => { const arr = [false, 1, "2"] as const; - function index() { - return Math.random() > 0.5 ? 0 : 1; - } + const index = 0 as 0 | 1 - const a = arr.at(index()); + const a = arr.at(index); type tests = [Expect>]; }); doNotExecute(async () => { const arr = [false, true, 1, "2"] as const; - function index() { - return Math.random() > 0.5 ? -1 : -2; - } + const index = -1 as -1 | -2 - const a = arr.at(index()); + const a = arr.at(index); type tests = [Expect>]; });