From d37bdd5d9db2151eb1c04cf97aad8d419f528bb5 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 26 Feb 2024 20:45:37 -0600 Subject: [PATCH 01/17] false negative for consts --- .../rules/no-unnecessary-type-assertion.ts | 20 ++++++--------- .../no-unnecessary-type-assertion.test.ts | 25 +++++++++++++++++-- 2 files changed, 31 insertions(+), 14 deletions(-) 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 4d79c84368e0..b9b24d151ec0 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -242,20 +242,16 @@ export default createRule({ const castType = services.getTypeAtLocation(node); + const { parent } = node.parent; + if ( - isTypeFlagSet(castType, ts.TypeFlags.Literal) || - (tsutils.isObjectType(castType) && - (tsutils.isObjectFlagSet(castType, ts.ObjectFlags.Tuple) || - couldBeTupleType(castType))) + // It's not safe to remove a cast to a literal type, unless we are in a `const` variable declaration, as that + // type would otherwise be widened without the cast. + ((parent?.type === AST_NODE_TYPES.VariableDeclaration && + parent.kind === 'const') || + !castType.isLiteral()) && + services.getTypeAtLocation(node.expression) === castType ) { - // It's not always safe to remove a cast to a literal type or tuple - // type, as those types are sometimes widened without the cast. - return; - } - - const uncastType = services.getTypeAtLocation(node.expression); - - if (uncastType === castType) { context.report({ node, messageId: 'unnecessaryAssertion', 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 3eb36636a839..0af0985f7c27 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 @@ -25,10 +25,21 @@ if ( const name = member.id as TSESTree.StringLiteral; } `, + ` + type Bar = 'bar'; + const data = { + x: 'foo' as 'foo', + y: 'bar' as Bar, + }; + `, + "[1, 2, 3, 4, 5].map(x => [x, 'A' + x] as [number, string]);", + ` + let x: Array<[number, string]> = [1, 2, 3, 4, 5].map( + x => [x, 'A' + x] as [number, string], + ); + `, 'const foo = 3 as number;', 'const foo = 3;', - 'const foo = <3>3;', - 'const foo = 3 as 3;', ` type Tuple = [3, 'hi', 'bye']; const foo = [3, 'hi', 'bye'] as Tuple; @@ -245,6 +256,16 @@ const item = arr[0]; ], invalid: [ + { + code: 'const foo = <3>3;', + output: 'const foo = 3;', + errors: [{ messageId: 'unnecessaryAssertion', line: 1, column: 13 }], + }, + { + code: 'const foo = 3 as 3;', + output: 'const foo = 3;', + errors: [{ messageId: 'unnecessaryAssertion', line: 1, column: 13 }], + }, { code: ` const foo = 3; From 60ac5e5258c7fe22a2b28db0e8c4d8369a4c8368 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 26 Feb 2024 20:53:21 -0600 Subject: [PATCH 02/17] add more testcases --- .../no-unnecessary-type-assertion.test.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 0af0985f7c27..ac63fac20b51 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 @@ -266,6 +266,28 @@ const item = arr[0]; output: 'const foo = 3;', errors: [{ messageId: 'unnecessaryAssertion', line: 1, column: 13 }], }, + { + code: ` + type Foo = 3; + const foo = 3; + `, + output: ` + type Foo = 3; + const foo = 3; + `, + errors: [{ messageId: 'unnecessaryAssertion', line: 3, column: 21 }], + }, + { + code: ` + type Foo = 3; + const foo = 3 as Foo; + `, + output: ` + type Foo = 3; + const foo = 3; + `, + errors: [{ messageId: 'unnecessaryAssertion', line: 3, column: 21 }], + }, { code: ` const foo = 3; From cedae4971eb323389b8fafae0ac8eb020c1fad57 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 26 Feb 2024 22:14:36 -0600 Subject: [PATCH 03/17] remove unused fn --- .../rules/no-unnecessary-type-assertion.ts | 31 ------------------- 1 file changed, 31 deletions(-) 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 b9b24d151ec0..1e8f688acb16 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -59,37 +59,6 @@ export default createRule({ const checker = services.program.getTypeChecker(); const compilerOptions = services.program.getCompilerOptions(); - /** - * Sometimes tuple types don't have ObjectFlags.Tuple set, like when they're being matched against an inferred type. - * So, in addition, check if there are integer properties 0..n and no other numeric keys - */ - function couldBeTupleType(type: ts.ObjectType): boolean { - const properties = type.getProperties(); - - if (properties.length === 0) { - return false; - } - let i = 0; - - for (; i < properties.length; ++i) { - const name = properties[i].name; - - if (String(i) !== name) { - if (i === 0) { - // if there are no integer properties, this is not a tuple - return false; - } - break; - } - } - for (; i < properties.length; ++i) { - if (String(+properties[i].name) === properties[i].name) { - return false; // if there are any other numeric properties, this is not a tuple - } - } - return true; - } - /** * Returns true if there's a chance the variable has been used before a value has been assigned to it */ From 39c7d4bd3985ac857fc5009ba4da6dd40c9bae27 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 26 Feb 2024 22:56:36 -0600 Subject: [PATCH 04/17] add NNA --- .../src/rules/no-unnecessary-type-assertion.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 1e8f688acb16..da41fe7f8e5e 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -211,13 +211,13 @@ export default createRule({ const castType = services.getTypeAtLocation(node); - const { parent } = node.parent; + const grandparent = node.parent.parent!; if ( // It's not safe to remove a cast to a literal type, unless we are in a `const` variable declaration, as that // type would otherwise be widened without the cast. - ((parent?.type === AST_NODE_TYPES.VariableDeclaration && - parent.kind === 'const') || + ((grandparent.type === AST_NODE_TYPES.VariableDeclaration && + grandparent.kind === 'const') || !castType.isLiteral()) && services.getTypeAtLocation(node.expression) === castType ) { From 9f01e2af4b483c6d4c8a8e129a539a927e8b8519 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Tue, 27 Feb 2024 07:51:28 -0600 Subject: [PATCH 05/17] split into two ifs --- .../src/rules/no-unnecessary-type-assertion.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 da41fe7f8e5e..2672cc12198a 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -216,11 +216,14 @@ export default createRule({ if ( // It's not safe to remove a cast to a literal type, unless we are in a `const` variable declaration, as that // type would otherwise be widened without the cast. - ((grandparent.type === AST_NODE_TYPES.VariableDeclaration && - grandparent.kind === 'const') || - !castType.isLiteral()) && - services.getTypeAtLocation(node.expression) === castType + castType.isLiteral() && + (grandparent.type !== AST_NODE_TYPES.VariableDeclaration || + grandparent.kind !== 'const') ) { + return; + } + + if (services.getTypeAtLocation(node.expression) === castType) { context.report({ node, messageId: 'unnecessaryAssertion', From b44cfe37406742741247d58c7ad1280abfe2090d Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Tue, 27 Feb 2024 07:51:43 -0600 Subject: [PATCH 06/17] restore comment --- .../eslint-plugin/src/rules/no-unnecessary-type-assertion.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2672cc12198a..031883d90f7c 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -214,12 +214,12 @@ export default createRule({ const grandparent = node.parent.parent!; if ( - // It's not safe to remove a cast to a literal type, unless we are in a `const` variable declaration, as that - // type would otherwise be widened without the cast. castType.isLiteral() && (grandparent.type !== AST_NODE_TYPES.VariableDeclaration || grandparent.kind !== 'const') ) { + // It's not safe to remove a cast to a literal type, unless we are in a `const` variable declaration, as that + // type would otherwise be widened without the cast. return; } From 78f87447f8640a430ce5cfab6f7d8f64b92fe583 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Tue, 27 Feb 2024 19:59:46 -0600 Subject: [PATCH 07/17] finish --- .../rules/no-unnecessary-type-assertion.ts | 27 +++--- .../no-unnecessary-type-assertion.test.ts | 87 +++++++++++++++++-- 2 files changed, 95 insertions(+), 19 deletions(-) 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 031883d90f7c..9ac323837cbd 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -106,6 +106,13 @@ export default createRule({ ); } + function isConstVariableDeclaration(node: TSESTree.Node): boolean { + return ( + node.type === AST_NODE_TYPES.VariableDeclaration && + node.kind === 'const' + ); + } + return { TSNonNullExpression(node): void { if ( @@ -203,27 +210,21 @@ export default createRule({ if ( options.typesToIgnore?.includes( context.sourceCode.getText(node.typeAnnotation), - ) || - isConstAssertion(node.typeAnnotation) + ) ) { return; } + const uncastType = services.getTypeAtLocation(node.expression); const castType = services.getTypeAtLocation(node); - const grandparent = node.parent.parent!; + const typeIsUnchanged = uncastType === castType; - if ( - castType.isLiteral() && - (grandparent.type !== AST_NODE_TYPES.VariableDeclaration || - grandparent.kind !== 'const') - ) { - // It's not safe to remove a cast to a literal type, unless we are in a `const` variable declaration, as that - // type would otherwise be widened without the cast. - return; - } + const wouldSameTypeBeInferred = castType.isLiteral() + ? isConstVariableDeclaration(node.parent.parent!) + : !isConstAssertion(node.typeAnnotation); - if (services.getTypeAtLocation(node.expression) === castType) { + if (typeIsUnchanged && wouldSameTypeBeInferred) { context.report({ node, messageId: 'unnecessaryAssertion', 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 ac63fac20b51..ddeb28ff4c4c 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 @@ -16,6 +16,27 @@ const ruleTester = new RuleTester({ ruleTester.run('no-unnecessary-type-assertion', rule, { valid: [ ` + let x = 1; // typeof x = number + let y = 1 as 1; // typeof y = 1 + const x = 1; // typeof x = 1 + let y = x; // typeof y = number + let z2 = x as 1; // okay - typeof z2 = 1 + const foo = 3 as number; + const foon = 3; + const foom = 3 as number; + `, + { + code: ` + const cx = 1; + // bad + let z0 = cx as number; + // bad, nice to check but not necessary + let z1 = cx as const; + // good + let z2 = cx as 1; + `, + }, + ` import { TSESTree } from '@typescript-eslint/utils'; declare const member: TSESTree.TSEnumMember; if ( @@ -185,9 +206,6 @@ const c = [...a, ...b] as const; { code: 'const a = [1, 2] as const;', }, - { - code: "const a = 'a' as const;", - }, { code: "const a = { foo: 'foo' } as const;", }, @@ -201,9 +219,6 @@ const c = [...a, ...b]; { code: 'const a = [1, 2];', }, - { - code: "const a = 'a';", - }, { code: "const a = { foo: 'foo' };", }, @@ -256,6 +271,66 @@ const item = arr[0]; ], invalid: [ + { + code: "const fool = 'foo' as const;", + output: "const fool = 'foo';", + errors: [{ messageId: 'unnecessaryAssertion', line: 1 }], + }, + { + code: "const a = 'a' as const;", + output: "const a = 'a';", + errors: [{ messageId: 'unnecessaryAssertion', line: 1 }], + }, + { + code: "const a = 'a';", + output: "const a = 'a';", + errors: [{ messageId: 'unnecessaryAssertion', line: 1 }], + }, + { + code: ` + type Foo = 3; + const foox: Foo = 3 as 3; + const fooy = 3 as Foo; + const fooz: Foo = 3; + const fooa = <3>3; + const foob = 3; + const fooc = 3; + const bar = fooc!; + function foo(x: number): number { + return x!; + } + function fook(x: number | undefined): number { + return x!; + } + const foos = fook(3) as number; + `, + output: ` + type Foo = 3; + const foox: Foo = 3; + const fooy = 3; + const fooz: Foo = 3; + const fooa = 3; + const foob = 3; + const fooc = 3; + const bar = fooc; + function foo(x: number): number { + return x; + } + function fook(x: number | undefined): number { + return x!; + } + const foos = fook(3); + `, + errors: [ + { messageId: 'unnecessaryAssertion', line: 3 }, + { messageId: 'unnecessaryAssertion', line: 4 }, + { messageId: 'unnecessaryAssertion', line: 6 }, + { messageId: 'unnecessaryAssertion', line: 7 }, + { messageId: 'unnecessaryAssertion', line: 9 }, + { messageId: 'unnecessaryAssertion', line: 11 }, + { messageId: 'unnecessaryAssertion', line: 16 }, + ], + }, { code: 'const foo = <3>3;', output: 'const foo = 3;', From 314d912182813403fedd1e55b92a073989d8a539 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Tue, 27 Feb 2024 20:52:29 -0600 Subject: [PATCH 08/17] change order for diffs --- .../eslint-plugin/src/rules/no-unnecessary-type-assertion.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 9ac323837cbd..bee933ef9d3e 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -215,9 +215,8 @@ export default createRule({ return; } - const uncastType = services.getTypeAtLocation(node.expression); const castType = services.getTypeAtLocation(node); - + const uncastType = services.getTypeAtLocation(node.expression); const typeIsUnchanged = uncastType === castType; const wouldSameTypeBeInferred = castType.isLiteral() From 3307af5ef67ed6574c99fd896119a308f03b2287 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Tue, 27 Feb 2024 21:24:37 -0600 Subject: [PATCH 09/17] fix error triggered by new lint logic --- packages/eslint-plugin/tests/rules/no-array-constructor.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts b/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts index 4f3589400893..6bc1d719e976 100644 --- a/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts +++ b/packages/eslint-plugin/tests/rules/no-array-constructor.test.ts @@ -7,7 +7,7 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); -const messageId = 'useLiteral' as const; +const messageId = 'useLiteral'; ruleTester.run('no-array-constructor', rule, { valid: [ From 5d7dd2a2ef5528a337b6bf0dd8f07f68045abc69 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 4 Mar 2024 06:43:08 -0600 Subject: [PATCH 10/17] WIP clean up tests --- .../no-unnecessary-type-assertion.test.ts | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) 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 ddeb28ff4c4c..6fe63970a015 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 @@ -16,27 +16,6 @@ const ruleTester = new RuleTester({ ruleTester.run('no-unnecessary-type-assertion', rule, { valid: [ ` - let x = 1; // typeof x = number - let y = 1 as 1; // typeof y = 1 - const x = 1; // typeof x = 1 - let y = x; // typeof y = number - let z2 = x as 1; // okay - typeof z2 = 1 - const foo = 3 as number; - const foon = 3; - const foom = 3 as number; - `, - { - code: ` - const cx = 1; - // bad - let z0 = cx as number; - // bad, nice to check but not necessary - let z1 = cx as const; - // good - let z2 = cx as 1; - `, - }, - ` import { TSESTree } from '@typescript-eslint/utils'; declare const member: TSESTree.TSEnumMember; if ( @@ -46,6 +25,12 @@ if ( const name = member.id as TSESTree.StringLiteral; } `, + ` + const cx = 1; + let z0 = cx as number; + let z1 = cx as const; + let z2 = cx as 1; + `, ` type Bar = 'bar'; const data = { @@ -59,6 +44,11 @@ if ( x => [x, 'A' + x] as [number, string], ); `, + 'let x = 1;', + 'let y = 1 as 1;', + 'const x = 1;', + 'let y = x;', + 'let z2 = x as 1;', 'const foo = 3 as number;', 'const foo = 3;', ` From aa1ea22c6d3053c01f1b256b220ef5dbad3d4fda Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 4 Mar 2024 06:45:43 -0600 Subject: [PATCH 11/17] WIP --- .../tests/rules/no-unnecessary-type-assertion.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 6fe63970a015..b2c16f2e701b 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 @@ -51,6 +51,8 @@ if ( 'let z2 = x as 1;', 'const foo = 3 as number;', 'const foo = 3;', + 'const foo = <3>3;', + 'const foo = 3 as 3;', ` type Tuple = [3, 'hi', 'bye']; const foo = [3, 'hi', 'bye'] as Tuple; @@ -261,11 +263,6 @@ const item = arr[0]; ], invalid: [ - { - code: "const fool = 'foo' as const;", - output: "const fool = 'foo';", - errors: [{ messageId: 'unnecessaryAssertion', line: 1 }], - }, { code: "const a = 'a' as const;", output: "const a = 'a';", From 37406fc04b552c6edacd57aa4777b8c388357d2e Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 4 Mar 2024 06:47:45 -0600 Subject: [PATCH 12/17] WIP --- .../tests/rules/no-unnecessary-type-assertion.test.ts | 6 ------ 1 file changed, 6 deletions(-) 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 b2c16f2e701b..51230409c99a 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 @@ -44,15 +44,9 @@ if ( x => [x, 'A' + x] as [number, string], ); `, - 'let x = 1;', 'let y = 1 as 1;', - 'const x = 1;', - 'let y = x;', - 'let z2 = x as 1;', 'const foo = 3 as number;', 'const foo = 3;', - 'const foo = <3>3;', - 'const foo = 3 as 3;', ` type Tuple = [3, 'hi', 'bye']; const foo = [3, 'hi', 'bye'] as Tuple; From 09f23b9eb30d21af656c7cf0a7b624de1e2067fb Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 4 Mar 2024 06:59:21 -0600 Subject: [PATCH 13/17] remove duplicate tests --- .../no-unnecessary-type-assertion.test.ts | 45 ------------------- 1 file changed, 45 deletions(-) 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 51230409c99a..002f28d7a551 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 @@ -267,51 +267,6 @@ const item = arr[0]; output: "const a = 'a';", errors: [{ messageId: 'unnecessaryAssertion', line: 1 }], }, - { - code: ` - type Foo = 3; - const foox: Foo = 3 as 3; - const fooy = 3 as Foo; - const fooz: Foo = 3; - const fooa = <3>3; - const foob = 3; - const fooc = 3; - const bar = fooc!; - function foo(x: number): number { - return x!; - } - function fook(x: number | undefined): number { - return x!; - } - const foos = fook(3) as number; - `, - output: ` - type Foo = 3; - const foox: Foo = 3; - const fooy = 3; - const fooz: Foo = 3; - const fooa = 3; - const foob = 3; - const fooc = 3; - const bar = fooc; - function foo(x: number): number { - return x; - } - function fook(x: number | undefined): number { - return x!; - } - const foos = fook(3); - `, - errors: [ - { messageId: 'unnecessaryAssertion', line: 3 }, - { messageId: 'unnecessaryAssertion', line: 4 }, - { messageId: 'unnecessaryAssertion', line: 6 }, - { messageId: 'unnecessaryAssertion', line: 7 }, - { messageId: 'unnecessaryAssertion', line: 9 }, - { messageId: 'unnecessaryAssertion', line: 11 }, - { messageId: 'unnecessaryAssertion', line: 16 }, - ], - }, { code: 'const foo = <3>3;', output: 'const foo = 3;', From e40b518fde313a0ab6f5b94334042ccf6d8cb784 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 7 Mar 2024 07:06:17 -0600 Subject: [PATCH 14/17] fix docs --- .../docs/rules/no-unnecessary-type-assertion.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md index 7f428c056243..0d2b6c4e31d0 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md @@ -35,6 +35,10 @@ type Foo = 3; const foo = 3 as Foo; ``` +```ts +const foo = 'foo' as const; +``` + ```ts function foo(x: number): number { return x!; // unnecessary non-null @@ -52,7 +56,7 @@ const foo = 3 as number; ``` ```ts -const foo = 'foo' as const; +let foo = 'foo' as const; ``` ```ts From 83a77d1ee7824c41058b5de8f93e3ec6808d19df Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 7 Mar 2024 07:06:44 -0600 Subject: [PATCH 15/17] make test cases more self-contained Co-authored-by: auvred <61150013+auvred@users.noreply.github.com> --- .../rules/no-unnecessary-type-assertion.test.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 002f28d7a551..8660c5d1f577 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 @@ -26,10 +26,16 @@ if ( } `, ` - const cx = 1; - let z0 = cx as number; - let z1 = cx as const; - let z2 = cx as 1; + const c = 1; + let z = c as number; + `, + ` + const c = 1; + let z = c as const; + `, + ` + const c = 1; + let z = c as 1; `, ` type Bar = 'bar'; From 9dcc70b91276404f5f8eeb6842dc1f90922ef5d0 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 7 Mar 2024 07:31:45 -0600 Subject: [PATCH 16/17] suppress lint error --- .../eslint-plugin/src/rules/no-unnecessary-type-assertion.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 d19c245853e5..151318e7f9bf 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -222,7 +222,8 @@ export default createRule({ const typeIsUnchanged = uncastType === castType; const wouldSameTypeBeInferred = castType.isLiteral() - ? isConstVariableDeclaration(node.parent.parent!) + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + isConstVariableDeclaration(node.parent.parent!) : !isConstAssertion(node.typeAnnotation); if (typeIsUnchanged && wouldSameTypeBeInferred) { From 60133ea778ba22fbf10aa550521539115cac9080 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Fri, 8 Mar 2024 06:58:26 -0600 Subject: [PATCH 17/17] remove unnecessary type assertions --- .../typescript-estree/tests/lib/persistentParse.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/typescript-estree/tests/lib/persistentParse.test.ts b/packages/typescript-estree/tests/lib/persistentParse.test.ts index 710b9c54ab5a..dbfd2831dea5 100644 --- a/packages/typescript-estree/tests/lib/persistentParse.test.ts +++ b/packages/typescript-estree/tests/lib/persistentParse.test.ts @@ -124,7 +124,7 @@ function baseTests( it('allows parsing of deeply nested new files', () => { const PROJECT_DIR = setup(tsConfigIncludeAll, false); - const bazSlashBar = 'baz/bar' as const; + const bazSlashBar = 'baz/bar'; // parse once to: assert the config as correct, and to make sure the program is setup expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); @@ -149,7 +149,7 @@ function baseTests( fs.mkdirSync(path.join(PROJECT_DIR, 'src', 'bat')); fs.mkdirSync(path.join(PROJECT_DIR, 'src', 'bat', 'baz')); - const bazSlashBar = 'bat/baz/bar' as const; + const bazSlashBar = 'bat/baz/bar'; // write a new file and attempt to parse it writeFile(PROJECT_DIR, bazSlashBar); @@ -159,7 +159,7 @@ function baseTests( it('allows renaming of files', () => { const PROJECT_DIR = setup(tsConfigIncludeAll, true); - const bazSlashBar = 'baz/bar' as const; + const bazSlashBar = 'baz/bar'; // parse once to: assert the config as correct, and to make sure the program is setup expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); @@ -291,7 +291,7 @@ describe('persistent parse', () => { it('handles tsconfigs with no includes/excludes (nested)', () => { const PROJECT_DIR = setup({}, false); - const bazSlashBar = 'baz/bar' as const; + const bazSlashBar = 'baz/bar'; // parse once to: assert the config as correct, and to make sure the program is setup expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow();