From 37f2db5b1cef0b7620e70447f5d2687085318e37 Mon Sep 17 00:00:00 2001 From: Craig Spence Date: Tue, 21 Feb 2023 13:40:37 +1300 Subject: [PATCH 1/4] Add Array.isArray improvement --- package.json | 5 ++++ readme.md | 37 ++++++++++++++++++++++++++++++ src/entrypoints/array-isarray.d.ts | 3 +++ src/entrypoints/recommended.d.ts | 1 + src/tests/array-isarray.ts | 15 ++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 src/entrypoints/array-isarray.d.ts create mode 100644 src/tests/array-isarray.ts diff --git a/package.json b/package.json index 8489bb7..157ee90 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,11 @@ "import": "./dist/array-includes.mjs", "default": "./dist/array-includes.js" }, + "./array-isarray": { + "types": "./dist/array-isarray.d.ts", + "import": "./dist/array-isarray.mjs", + "default": "./dist/array-isarray.js" + }, "./set-has": { "types": "./dist/set-has.d.ts", "import": "./dist/set-has.mjs", diff --git a/readme.md b/readme.md index 9865f8e..fe6a5a3 100644 --- a/readme.md +++ b/readme.md @@ -196,6 +196,43 @@ const isUser = (input: string) => { }; ``` +### Make `Array.isArray` correctly narrow with `ReadonlyArray` + +```ts +import "@total-typescript/ts-reset/array-includes"; +``` + +This rule improves on TypeScript's default `Array.isArray` behaviour. Without this rule enabled, TypeScript will not include `ReadonlyArray` when narrowing Array types. + +```ts +// BEFORE +function makeArray(input: string | ReadonlyArray | Array) { + if (Array.isArray(input)) { + return input; + } + return [input]; +} + +type Result = ReturnType; // (string | readonly string[])[] +``` + +This is almost definitely not what the user intended! While it works correctly at run-time (since JS has no concept of ReadonlyArray), the inferred types are very incorrect. + +```ts +// AFTER +import "@total-typescript/ts-reset/array-includes"; + +function makeArray(input: string | ReadonlyArray | Array) { + // Array.isArray now + if (Array.isArray(input)) { + return input; + } + return [input]; +} + +type Result = ReturnType; // string[] +``` + ### Make `Set.has()` less strict ```ts diff --git a/src/entrypoints/array-isarray.d.ts b/src/entrypoints/array-isarray.d.ts new file mode 100644 index 0000000..c185fd8 --- /dev/null +++ b/src/entrypoints/array-isarray.d.ts @@ -0,0 +1,3 @@ +interface ArrayConstructor { + isArray(arg: any): arg is ReadonlyArray | Array; +} diff --git a/src/entrypoints/recommended.d.ts b/src/entrypoints/recommended.d.ts index 6eabca1..329633d 100644 --- a/src/entrypoints/recommended.d.ts +++ b/src/entrypoints/recommended.d.ts @@ -3,4 +3,5 @@ /// /// /// +/// /// diff --git a/src/tests/array-isarray.ts b/src/tests/array-isarray.ts new file mode 100644 index 0000000..270df16 --- /dev/null +++ b/src/tests/array-isarray.ts @@ -0,0 +1,15 @@ +import { doNotExecute, Equal, Expect } from "./utils"; + +doNotExecute(async () => { + function makeArray(input: Item | ReadonlyArray | Array) { + if (Array.isArray(input)) { + return input; + } + return [input]; + } + + const [first] = makeArray([{ a: "1" }, { a: "2" }, { a: "3" }] as const); + + // No error! + first.a; +}); From 3b86f08d840b7c4d66fe533594b43724a577e0a4 Mon Sep 17 00:00:00 2001 From: Craig Spence Date: Tue, 21 Feb 2023 14:00:10 +1300 Subject: [PATCH 2/4] Move to other is-array rule --- package.json | 5 -- readme.md | 74 +++++++++++++++--------------- src/entrypoints/array-isarray.d.ts | 3 -- src/entrypoints/is-array.d.ts | 2 +- src/tests/array-isarray.ts | 15 ------ src/tests/is-array.ts | 18 +++++++- 6 files changed, 55 insertions(+), 62 deletions(-) delete mode 100644 src/entrypoints/array-isarray.d.ts delete mode 100644 src/tests/array-isarray.ts diff --git a/package.json b/package.json index 157ee90..8489bb7 100644 --- a/package.json +++ b/package.json @@ -53,11 +53,6 @@ "import": "./dist/array-includes.mjs", "default": "./dist/array-includes.js" }, - "./array-isarray": { - "types": "./dist/array-isarray.d.ts", - "import": "./dist/array-isarray.mjs", - "default": "./dist/array-isarray.js" - }, "./set-has": { "types": "./dist/set-has.d.ts", "import": "./dist/set-has.mjs", diff --git a/readme.md b/readme.md index fe6a5a3..4e674e8 100644 --- a/readme.md +++ b/readme.md @@ -196,43 +196,6 @@ const isUser = (input: string) => { }; ``` -### Make `Array.isArray` correctly narrow with `ReadonlyArray` - -```ts -import "@total-typescript/ts-reset/array-includes"; -``` - -This rule improves on TypeScript's default `Array.isArray` behaviour. Without this rule enabled, TypeScript will not include `ReadonlyArray` when narrowing Array types. - -```ts -// BEFORE -function makeArray(input: string | ReadonlyArray | Array) { - if (Array.isArray(input)) { - return input; - } - return [input]; -} - -type Result = ReturnType; // (string | readonly string[])[] -``` - -This is almost definitely not what the user intended! While it works correctly at run-time (since JS has no concept of ReadonlyArray), the inferred types are very incorrect. - -```ts -// AFTER -import "@total-typescript/ts-reset/array-includes"; - -function makeArray(input: string | ReadonlyArray | Array) { - // Array.isArray now - if (Array.isArray(input)) { - return input; - } - return [input]; -} - -type Result = ReturnType; // string[] -``` - ### Make `Set.has()` less strict ```ts @@ -293,6 +256,43 @@ const validate = (input: unknown) => { }; ``` +### Make `Array.isArray` correctly narrow with `ReadonlyArray` + +```ts +import "@total-typescript/ts-reset/array-includes"; +``` + +This rule improves on TypeScript's default `Array.isArray` behaviour. Without this rule enabled, TypeScript will not include `ReadonlyArray` when narrowing Array types. + +```ts +// BEFORE +function makeArray(input: string | ReadonlyArray | Array) { + if (Array.isArray(input)) { + return input; + } + return [input]; +} + +type Result = ReturnType; // (string | readonly string[])[] +``` + +This is almost definitely not what the user intended! While it works correctly at run-time (since JS has no concept of ReadonlyArray), the inferred types are very incorrect. + +```ts +// AFTER +import "@total-typescript/ts-reset/array-includes"; + +function makeArray(input: string | ReadonlyArray | Array) { + // Array.isArray now + if (Array.isArray(input)) { + return input; + } + return [input]; +} + +type Result = ReturnType; // string[] +``` + ## Rules we won't add ### `Object.keys`/`Object.entries` diff --git a/src/entrypoints/array-isarray.d.ts b/src/entrypoints/array-isarray.d.ts deleted file mode 100644 index c185fd8..0000000 --- a/src/entrypoints/array-isarray.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -interface ArrayConstructor { - isArray(arg: any): arg is ReadonlyArray | Array; -} diff --git a/src/entrypoints/is-array.d.ts b/src/entrypoints/is-array.d.ts index 199bc66..c185fd8 100644 --- a/src/entrypoints/is-array.d.ts +++ b/src/entrypoints/is-array.d.ts @@ -1,3 +1,3 @@ interface ArrayConstructor { - isArray(arg: any): arg is unknown[]; + isArray(arg: any): arg is ReadonlyArray | Array; } diff --git a/src/tests/array-isarray.ts b/src/tests/array-isarray.ts deleted file mode 100644 index 270df16..0000000 --- a/src/tests/array-isarray.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { doNotExecute, Equal, Expect } from "./utils"; - -doNotExecute(async () => { - function makeArray(input: Item | ReadonlyArray | Array) { - if (Array.isArray(input)) { - return input; - } - return [input]; - } - - const [first] = makeArray([{ a: "1" }, { a: "2" }, { a: "3" }] as const); - - // No error! - first.a; -}); diff --git a/src/tests/is-array.ts b/src/tests/is-array.ts index 58e3160..5e8025a 100644 --- a/src/tests/is-array.ts +++ b/src/tests/is-array.ts @@ -4,7 +4,9 @@ doNotExecute(() => { const maybeArr = [1, 2, 3] as unknown; if (Array.isArray(maybeArr)) { - type tests = [Expect>]; + type tests = [ + Expect> + ]; } }); @@ -15,3 +17,17 @@ doNotExecute(() => { type tests = [Expect>]; } }); + +doNotExecute(async () => { + function makeArray(input: Item | ReadonlyArray | Array) { + if (Array.isArray(input)) { + return input; + } + return [input]; + } + + const [first] = makeArray([{ a: "1" }, { a: "2" }, { a: "3" }] as const); + + // No error! + first.a; +}); From b7fe6826008198f96a17b0740a92334ba903a0c5 Mon Sep 17 00:00:00 2001 From: Craig Spence Date: Tue, 21 Feb 2023 14:00:34 +1300 Subject: [PATCH 3/4] Move to other is-array rule --- src/entrypoints/recommended.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entrypoints/recommended.d.ts b/src/entrypoints/recommended.d.ts index 329633d..6eabca1 100644 --- a/src/entrypoints/recommended.d.ts +++ b/src/entrypoints/recommended.d.ts @@ -3,5 +3,4 @@ /// /// /// -/// /// From 3e221550674ea07b49be38c04eafb79bd4b1e0fd Mon Sep 17 00:00:00 2001 From: Craig Spence Date: Tue, 21 Feb 2023 16:26:02 -0600 Subject: [PATCH 4/4] Update readme.md Co-authored-by: Tim Buckley --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4e674e8..fd5adb7 100644 --- a/readme.md +++ b/readme.md @@ -262,7 +262,7 @@ const validate = (input: unknown) => { import "@total-typescript/ts-reset/array-includes"; ``` -This rule improves on TypeScript's default `Array.isArray` behaviour. Without this rule enabled, TypeScript will not include `ReadonlyArray` when narrowing Array types. +This rule improves on TypeScript's default `Array.isArray` behaviour. With this rule enabled, TypeScript will now include `ReadonlyArray` when narrowing Array types. ```ts // BEFORE