diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index fdb957b4602e..a73962b9acbd 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -128,6 +128,38 @@ export default createRule({ ); } + function isTypeUnchanged(uncast: ts.Type, cast: ts.Type): boolean { + if (uncast === cast) { + return true; + } + + if ( + isTypeFlagSet(uncast, ts.TypeFlags.Undefined) && + isTypeFlagSet(cast, ts.TypeFlags.Undefined) && + tsutils.isCompilerOptionEnabled( + compilerOptions, + 'exactOptionalPropertyTypes', + ) + ) { + const uncastParts = tsutils + .unionTypeParts(uncast) + .filter(part => !isTypeFlagSet(part, ts.TypeFlags.Undefined)); + + const castParts = tsutils + .unionTypeParts(cast) + .filter(part => !isTypeFlagSet(part, ts.TypeFlags.Undefined)); + + if (uncastParts.length !== castParts.length) { + return false; + } + + const uncastPartsSet = new Set(uncastParts); + return castParts.every(part => uncastPartsSet.has(part)); + } + + return false; + } + return { TSNonNullExpression(node): void { if ( @@ -232,7 +264,7 @@ export default createRule({ const castType = services.getTypeAtLocation(node); const uncastType = services.getTypeAtLocation(node.expression); - const typeIsUnchanged = uncastType === castType; + const typeIsUnchanged = isTypeUnchanged(uncastType, castType); const wouldSameTypeBeInferred = castType.isLiteral() ? isLiteralVariableDeclarationChangingTypeWithConst(node) diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index a0dd0755239e..17efb3ff0baf 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -19,6 +19,11 @@ const optionsWithOnUncheckedIndexedAccess = { project: './tsconfig.noUncheckedIndexedAccess.json', }; +const optionsWithExactOptionalPropertyTypes = { + tsconfigRootDir: rootDir, + project: './tsconfig.exactOptionalPropertyTypes.json', +}; + ruleTester.run('no-unnecessary-type-assertion', rule, { valid: [ ` @@ -287,6 +292,51 @@ const templateLiteral = \`\${myString}-somethingElse\` as const; const myString = 'foo'; const templateLiteral = \`\${myString}-somethingElse\`; `, + { + code: ` +declare const foo: { + a?: string; +}; +const bar = foo.a as string; + `, + parserOptions: optionsWithExactOptionalPropertyTypes, + }, + { + code: ` +declare const foo: { + a?: string | undefined; +}; +const bar = foo.a as string; + `, + parserOptions: optionsWithExactOptionalPropertyTypes, + }, + { + code: ` +declare const foo: { + a: string; +}; +const bar = foo.a as string | undefined; + `, + parserOptions: optionsWithExactOptionalPropertyTypes, + }, + { + code: ` +declare const foo: { + a?: string | null | number; +}; +const bar = foo.a as string | undefined; + `, + parserOptions: optionsWithExactOptionalPropertyTypes, + }, + { + code: ` +declare const foo: { + a?: string | number; +}; +const bar = foo.a as string | undefined | bigint; + `, + parserOptions: optionsWithExactOptionalPropertyTypes, + }, ], invalid: [ @@ -934,5 +984,50 @@ function bar(items: string[]) { }, ], }, + // exactOptionalPropertyTypes = true + { + code: ` +declare const foo: { + a?: string; +}; +const bar = foo.a as string | undefined; + `, + output: ` +declare const foo: { + a?: string; +}; +const bar = foo.a; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 5, + column: 13, + }, + ], + parserOptions: optionsWithExactOptionalPropertyTypes, + }, + { + code: ` +declare const foo: { + a?: string | undefined; +}; +const bar = foo.a as string | undefined; + `, + output: ` +declare const foo: { + a?: string | undefined; +}; +const bar = foo.a; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 5, + column: 13, + }, + ], + parserOptions: optionsWithExactOptionalPropertyTypes, + }, ], }); 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