From 32dfcf4af5d46b6d39586b0e56578234b0701d97 Mon Sep 17 00:00:00 2001 From: Daniel Cassidy Date: Thu, 3 Feb 2022 10:07:26 +0000 Subject: [PATCH 1/4] fix(eslint-plugin): fix typo --- .../src/rules/non-nullable-type-assertion-style.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts b/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts index 69fc21736de..0db2d93de8f 100644 --- a/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts +++ b/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts @@ -17,7 +17,7 @@ export default util.createRule({ fixable: 'code', messages: { preferNonNullAssertion: - 'Use a ! assertion to more succintly remove null and undefined from the type.', + 'Use a ! assertion to more succinctly remove null and undefined from the type.', }, schema: [], type: 'suggestion', From ce4706e543c65084ebc6f93327cecb26ca43cce0 Mon Sep 17 00:00:00 2001 From: Daniel Cassidy Date: Thu, 3 Feb 2022 12:07:11 +0000 Subject: [PATCH 2/4] fix(eslint-plugin): non-nullable-type-assertion-style: don't complain when casting to a type parameter that might be nullish --- .../non-nullable-type-assertion-style.ts | 26 +++++++++++++-- .../tsconfig.noUncheckedIndexedAccess.json | 6 ++++ .../non-nullable-type-assertion-style.test.ts | 33 ++++++++++++++++++- 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 packages/eslint-plugin/tests/fixtures/tsconfig.noUncheckedIndexedAccess.json diff --git a/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts b/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts index 0db2d93de8f..b0649d05745 100644 --- a/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts +++ b/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts @@ -43,14 +43,31 @@ export default util.createRule({ return tsutils.unionTypeParts(type); }; + const couldBeNullish = (type: ts.Type): boolean => { + if (type.flags & ts.TypeFlags.TypeParameter) { + const constraint = type.getConstraint(); + return constraint == null || couldBeNullish(constraint); + } else if (tsutils.isUnionType(type)) { + for (const part of type.types) { + if (couldBeNullish(part)) { + return true; + } + } + return false; + } else { + return ( + (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0 + ); + } + }; + const sameTypeWithoutNullish = ( assertedTypes: ts.Type[], originalTypes: ts.Type[], ): boolean => { const nonNullishOriginalTypes = originalTypes.filter( type => - type.flags !== ts.TypeFlags.Null && - type.flags !== ts.TypeFlags.Undefined, + (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0, ); if (nonNullishOriginalTypes.length === originalTypes.length) { @@ -58,7 +75,10 @@ export default util.createRule({ } for (const assertedType of assertedTypes) { - if (!nonNullishOriginalTypes.includes(assertedType)) { + if ( + couldBeNullish(assertedType) || + !nonNullishOriginalTypes.includes(assertedType) + ) { return false; } } diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig.noUncheckedIndexedAccess.json b/packages/eslint-plugin/tests/fixtures/tsconfig.noUncheckedIndexedAccess.json new file mode 100644 index 00000000000..c452514f949 --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/tsconfig.noUncheckedIndexedAccess.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noUncheckedIndexedAccess": true + } +} diff --git a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts index fadd5064abd..117f0afdb28 100644 --- a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts +++ b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts @@ -7,7 +7,7 @@ const ruleTester = new RuleTester({ parserOptions: { sourceType: 'module', tsconfigRootDir: rootDir, - project: './tsconfig.json', + project: './tsconfig.noUncheckedIndexedAccess.json', }, parser: '@typescript-eslint/parser', }); @@ -61,6 +61,16 @@ const x = 1 as 1; declare function foo(): T; const bar = foo() as number; `, + ` +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0] as T) : null; +} + `, + ` +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0] as T) : null; +} + `, ], invalid: [ @@ -199,5 +209,26 @@ declare const x: T; const y = x!; `, }, + { + code: ` +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0] as T) : null; +} + `, + errors: [ + { + column: 30, + line: 3, + messageId: 'preferNonNullAssertion', + }, + ], + // Output is not expected to match required formatting due to excess parentheses + // eslint-disable-next-line @typescript-eslint/internal/plugin-test-formatting + output: ` +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0]!) : null; +} + `, + }, ], }); From 754d48dc8f1894c78f4cb216c8fba89401f273fd Mon Sep 17 00:00:00 2001 From: Daniel Cassidy Date: Fri, 4 Feb 2022 10:54:44 +0000 Subject: [PATCH 3/4] fix(eslint-plugin): non-nullable-type-assertion-style: add tests for `string | undefined` and `string | null | undefined` --- .../rules/non-nullable-type-assertion-style.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts index 117f0afdb28..af3acda08f6 100644 --- a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts +++ b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts @@ -69,6 +69,18 @@ function first(array: ArrayLike): T | null { ` function first(array: ArrayLike): T | null { return array.length > 0 ? (array[0] as T) : null; +} + `, + ` +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0] as T) : null; +} + `, + ` +function first( + array: ArrayLike, +): T | null { + return array.length > 0 ? (array[0] as T) : null; } `, ], From 8befbe2ef8cec1e3db97e24f48d53fe5fc95b9e5 Mon Sep 17 00:00:00 2001 From: Daniel Cassidy Date: Fri, 4 Feb 2022 11:05:31 +0000 Subject: [PATCH 4/4] fix(eslint-plugin): non-nullable-type-assertion-style: add test for nested union type --- .../tests/rules/non-nullable-type-assertion-style.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts index af3acda08f6..9bfa209168d 100644 --- a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts +++ b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts @@ -81,6 +81,13 @@ function first( array: ArrayLike, ): T | null { return array.length > 0 ? (array[0] as T) : null; +} + `, + ` +type A = 'a' | 'A'; +type B = 'b' | 'B'; +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0] as T) : null; } `, ],