diff --git a/readme.md b/readme.md index 9865f8e..fd5adb7 100644 --- a/readme.md +++ b/readme.md @@ -256,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. With this rule enabled, TypeScript will now 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/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/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; +});