From cb2256168c67e0383083673a5afe77076de49da5 Mon Sep 17 00:00:00 2001 From: Zen <843968788@qq.com> Date: Tue, 9 Mar 2021 01:35:23 +0800 Subject: [PATCH] fix(eslint-plugin): [no-unnecessary-type-assertion] handle assignment (#3133) --- .../rules/no-unnecessary-type-assertion.ts | 19 +++++++++ packages/eslint-plugin/src/util/types.ts | 8 ++++ .../no-unnecessary-type-assertion.test.ts | 40 +++++++++++++++++++ 3 files changed, 67 insertions(+) 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 634d8b890ce..5ae12f5a33d 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -135,7 +135,26 @@ export default util.createRule({ return { TSNonNullExpression(node): void { + if ( + node.parent?.type === AST_NODE_TYPES.AssignmentExpression && + node.parent?.operator === '=' && + node.parent.left === node + ) { + context.report({ + node, + messageId: 'contextuallyUnnecessary', + fix(fixer) { + return fixer.removeRange([ + node.expression.range[1], + node.range[1], + ]); + }, + }); + return; + } + const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); + const type = util.getConstrainedTypeAtLocation( checker, originalNode.expression, diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts index d29349f812f..920ce66de02 100644 --- a/packages/eslint-plugin/src/util/types.ts +++ b/packages/eslint-plugin/src/util/types.ts @@ -11,6 +11,7 @@ import { isVariableDeclaration, unionTypeParts, isPropertyAssignment, + isBinaryExpression, } from 'tsutils'; import * as ts from 'typescript'; @@ -498,6 +499,13 @@ export function getContextualType( return checker.getContextualType(parent); } else if (isPropertyAssignment(parent) && isIdentifier(node)) { return checker.getContextualType(node); + } else if ( + isBinaryExpression(parent) && + parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && + parent.right === node + ) { + // is RHS of assignment + return checker.getTypeAtLocation(parent.left); } else if ( ![ts.SyntaxKind.TemplateSpan, ts.SyntaxKind.JsxExpression].includes( parent.kind, 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 f5671dd5d6b..65d0874dd3a 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 @@ -186,6 +186,22 @@ const c = [...a, ...b]; { code: "const a = { foo: 'foo' };", }, + { + code: ` +let a: number | undefined; +let b: number | undefined; +let c: number; +a = b; +c = b!; +a! -= 1; + `, + }, + { + code: ` +let a: { b?: string } | undefined; +a!.b = ''; + `, + }, ], invalid: [ @@ -451,5 +467,29 @@ function Test(props: { id?: string | number }) { ], filename: 'react.tsx', }, + { + code: ` +let x: number | undefined; +let y: number | undefined; +y = x!; +y! = 0; + `, + output: ` +let x: number | undefined; +let y: number | undefined; +y = x; +y = 0; + `, + errors: [ + { + messageId: 'contextuallyUnnecessary', + line: 4, + }, + { + messageId: 'contextuallyUnnecessary', + line: 5, + }, + ], + }, ], });