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 69fc21736ded..b0649d05745d 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', @@ -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 000000000000..c452514f9499 --- /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 fadd5064abd1..9bfa209168dd 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,35 @@ 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; +} + `, + ` +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; +} + `, + ` +type A = 'a' | 'A'; +type B = 'b' | 'B'; +function first(array: ArrayLike): T | null { + return array.length > 0 ? (array[0] as T) : null; +} + `, ], invalid: [ @@ -199,5 +228,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; +} + `, + }, ], }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy