-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(eslint-plugin): [no-unused-vars] no is assigned a value but only used as a type
error when it has a same name
#11322
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
base: main
Are you sure you want to change the base?
Changes from all commits
f7d55fd
bc379fc
bfffde3
2f474b3
2bfdd0b
52eede8
df466a4
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 | ||
---|---|---|---|---|
@@ -1,10 +1,13 @@ | ||||
import type { | ||||
ScopeManager, | ||||
ScopeVariable, | ||||
Variable, | ||||
} from '@typescript-eslint/scope-manager'; | ||||
import type { TSESTree } from '@typescript-eslint/utils'; | ||||
|
||||
import { | ||||
DefinitionType, | ||||
ESLintScopeVariable, | ||||
ImplicitLibVariable, | ||||
ScopeType, | ||||
Visitor, | ||||
|
@@ -188,7 +191,7 @@ class UnusedVarsVisitor extends Visitor { | |||
// basic exported variables | ||||
isExported(variable) || | ||||
// variables implicitly exported via a merged declaration | ||||
isMergableExported(variable) || | ||||
isMergeableExported(variable) || | ||||
// used variables | ||||
isUsedVariable(variable) | ||||
) { | ||||
|
@@ -415,6 +418,26 @@ function isSelfReference( | |||
return false; | ||||
} | ||||
|
||||
const exportExceptDefTypes: DefinitionType[] = [ | ||||
DefinitionType.Variable, | ||||
DefinitionType.Type, | ||||
]; | ||||
/** | ||||
* In edge cases, the existing used logic does not work. | ||||
* When the type and variable name of the variable are the same | ||||
* @ref https://github.com/typescript-eslint/typescript-eslint/issues/10658 | ||||
* @param variable the variable to check | ||||
* @returns true if it is an edge case | ||||
*/ | ||||
function isSafeUnusedExportCondition(variable: Variable): boolean { | ||||
if (variable.isTypeVariable && variable.isValueVariable) { | ||||
return !variable.defs | ||||
.map(d => d.type) | ||||
.every(t => exportExceptDefTypes.includes(t)); | ||||
} | ||||
return true; | ||||
} | ||||
|
||||
const MERGABLE_TYPES = new Set([ | ||||
AST_NODE_TYPES.ClassDeclaration, | ||||
AST_NODE_TYPES.FunctionDeclaration, | ||||
|
@@ -426,7 +449,7 @@ const MERGABLE_TYPES = new Set([ | |||
* Determine if the variable is directly exported | ||||
* @param variable the variable to check | ||||
*/ | ||||
function isMergableExported(variable: ScopeVariable): boolean { | ||||
function isMergeableExported(variable: Variable): boolean { | ||||
// If all of the merged things are of the same type, TS will error if not all of them are exported - so we only need to find one | ||||
for (const def of variable.defs) { | ||||
// parameters can never be exported. | ||||
|
@@ -441,7 +464,10 @@ function isMergableExported(variable: ScopeVariable): boolean { | |||
def.node.parent?.type === AST_NODE_TYPES.ExportNamedDeclaration) || | ||||
def.node.parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration | ||||
) { | ||||
return true; | ||||
return ( | ||||
def.node.type !== AST_NODE_TYPES.TSTypeAliasDeclaration || | ||||
isSafeUnusedExportCondition(variable) | ||||
); | ||||
} | ||||
} | ||||
|
||||
|
@@ -453,7 +479,7 @@ function isMergableExported(variable: ScopeVariable): boolean { | |||
* @param variable eslint-scope variable object. | ||||
* @returns True if the variable is exported, false if not. | ||||
*/ | ||||
function isExported(variable: ScopeVariable): boolean { | ||||
function isExported(variable: Variable): boolean { | ||||
return variable.defs.some(definition => { | ||||
let node = definition.node; | ||||
|
||||
|
@@ -465,7 +491,13 @@ function isExported(variable: ScopeVariable): boolean { | |||
} | ||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||||
return node.parent!.type.startsWith('Export'); | ||||
const isExportedFlag = node.parent!.type.startsWith('Export'); | ||||
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. need adviceIs there a reason why didn't use optional chaining before? 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. tl;dr: the Other code using Digging in for fun... To see what node type(s) don't know that the node;
// ^? let node: TSESTree.CatchClause | TSESTree.ArrayPattern | TSESTree.ObjectPattern | TSESTree.Identifier | TSESTree.ClassDeclarationWithName | ... 192 more ... | TSESTree.YieldExpression
if (!node.parent) {
node;
// ^? let node: TSESTree.Program
}
Its definition comes from:
I think the root issue, then, is that Filed: #11334. Once that's fixed then lint rules will force this code to remove unnecessary |
||||
|
||||
return ( | ||||
isExportedFlag && | ||||
(node.type !== AST_NODE_TYPES.TSTypeAliasDeclaration || | ||||
isSafeUnusedExportCondition(variable)) | ||||
); | ||||
}); | ||||
} | ||||
|
||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Testing] If I comment out this
.every
, then all tests still pass. In fact, if the inside of theif
just doesreturn false;
then tests still pass. So either this is unnecessary code or there's a gap in test coverage.