-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(eslint-plugin): [no-unnecessary-type-assertion] fix false negative for const variable declarations #8558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d37bdd5
60ac5e5
cedae49
39c7d4b
9f01e2a
b44cfe3
054f71c
78f8744
314d912
3307af5
5d7dd2a
aa1ea22
37406fc
09f23b9
898de07
e40b518
83a77d1
9dcc70b
fadd011
5e04444
60133ea
69f2cca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,37 +61,6 @@ export default createRule<Options, MessageIds>({ | |
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 | ||
*/ | ||
|
@@ -139,6 +108,13 @@ export default createRule<Options, MessageIds>({ | |
); | ||
} | ||
|
||
function isConstVariableDeclaration(node: TSESTree.Node): boolean { | ||
return ( | ||
node.type === AST_NODE_TYPES.VariableDeclaration && | ||
node.kind === 'const' | ||
); | ||
} | ||
|
||
Comment on lines
+111
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [non-actionable] Hmmm, looks like these cases are not reported (playground) let a: unknown
const b = (a = 1 as 1)
const c = (console.log(1), 1 as 1) However, I'm not sure we can easily detect all such cases. This function might become too complex... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think we can leave these for now. If someone files an issue showing them being buggy in isolation, then that'd indicate there's enough user appetite to start thinking about whether the complexity is worth it. |
||
return { | ||
TSNonNullExpression(node): void { | ||
if ( | ||
|
@@ -236,28 +212,21 @@ export default createRule<Options, MessageIds>({ | |
if ( | ||
options.typesToIgnore?.includes( | ||
context.sourceCode.getText(node.typeAnnotation), | ||
) || | ||
isConstAssertion(node.typeAnnotation) | ||
) | ||
) { | ||
return; | ||
} | ||
|
||
const castType = services.getTypeAtLocation(node); | ||
|
||
if ( | ||
isTypeFlagSet(castType, ts.TypeFlags.Literal) || | ||
(tsutils.isObjectType(castType) && | ||
(tsutils.isObjectFlagSet(castType, ts.ObjectFlags.Tuple) || | ||
couldBeTupleType(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; | ||
abrahamguo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
const uncastType = services.getTypeAtLocation(node.expression); | ||
const typeIsUnchanged = uncastType === castType; | ||
|
||
const wouldSameTypeBeInferred = castType.isLiteral() | ||
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
isConstVariableDeclaration(node.parent.parent!) | ||
: !isConstAssertion(node.typeAnnotation); | ||
|
||
if (uncastType === castType) { | ||
if (typeIsUnchanged && wouldSameTypeBeInferred) { | ||
context.report({ | ||
node, | ||
messageId: 'unnecessaryAssertion', | ||
|
Uh oh!
There was an error while loading. Please reload this page.