From dda9cee68a5cd78b358a854027999c739ac623e9 Mon Sep 17 00:00:00 2001 From: Rafael Santana Date: Tue, 21 Sep 2021 03:54:56 -0300 Subject: [PATCH] fix(eslint-plugin): [no-shadow] ignore type-only imports properly (#3868) --- packages/eslint-plugin/src/rules/no-shadow.ts | 25 ++++++++-- .../tests/rules/no-shadow.test.ts | 46 ++++++++++++++++++- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-shadow.ts b/packages/eslint-plugin/src/rules/no-shadow.ts index 6eb2ef38f7c..ea27f87e083 100644 --- a/packages/eslint-plugin/src/rules/no-shadow.ts +++ b/packages/eslint-plugin/src/rules/no-shadow.ts @@ -1,9 +1,14 @@ import { - TSESTree, - TSESLint, AST_NODE_TYPES, + TSESLint, + TSESTree, } from '@typescript-eslint/experimental-utils'; -import { ScopeType } from '@typescript-eslint/scope-manager'; +import { + Definition, + DefinitionType, + ImportBindingDefinition, + ScopeType, +} from '@typescript-eslint/scope-manager'; import * as util from '../util'; type MessageIds = 'noShadow'; @@ -85,6 +90,15 @@ export default util.createRule({ return variable.defs[0].type === 'Parameter' && variable.name === 'this'; } + function isTypeImport( + definition: Definition, + ): definition is ImportBindingDefinition { + return ( + definition.type === DefinitionType.ImportBinding && + definition.parent.importKind === 'type' + ); + } + function isTypeValueShadow( variable: TSESLint.Scope.Variable, shadowed: TSESLint.Scope.Variable, @@ -98,8 +112,11 @@ export default util.createRule({ return false; } + const [firstDefinition] = shadowed.defs; const isShadowedValue = - 'isValueVariable' in shadowed ? shadowed.isValueVariable : true; + !('isValueVariable' in shadowed) || + !firstDefinition || + (!isTypeImport(firstDefinition) && shadowed.isValueVariable); return variable.isValueVariable !== isShadowedValue; } diff --git a/packages/eslint-plugin/tests/rules/no-shadow.test.ts b/packages/eslint-plugin/tests/rules/no-shadow.test.ts index f48adfc1b57..c3e5a09f344 100644 --- a/packages/eslint-plugin/tests/rules/no-shadow.test.ts +++ b/packages/eslint-plugin/tests/rules/no-shadow.test.ts @@ -1,6 +1,6 @@ +import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; import rule from '../../src/rules/no-shadow'; import { RuleTester } from '../RuleTester'; -import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; const ruleTester = new RuleTester({ parserOptions: { @@ -172,6 +172,18 @@ export class Wrapper { } } `, + { + // https://github.com/typescript-eslint/typescript-eslint/issues/3862 + code: ` +import type { foo } from './foo'; +type bar = number; + +// 'foo' is already declared in the upper scope +// 'bar' is fine +function doThing(foo: number, bar: number) {} + `, + options: [{ ignoreTypeValueShadow: true }], + }, ], invalid: [ { @@ -1398,5 +1410,37 @@ function foo(cb) { }, ], }, + { + code: ` +import type { foo } from './foo'; +function doThing(foo: number, bar: number) {} + `, + options: [{ ignoreTypeValueShadow: false }], + errors: [ + { + messageId: 'noShadow', + data: { name: 'foo' }, + type: AST_NODE_TYPES.Identifier, + line: 3, + column: 18, + }, + ], + }, + { + code: ` +import { foo } from './foo'; +function doThing(foo: number, bar: number) {} + `, + options: [{ ignoreTypeValueShadow: true }], + errors: [ + { + messageId: 'noShadow', + data: { name: 'foo' }, + type: AST_NODE_TYPES.Identifier, + line: 3, + column: 18, + }, + ], + }, ], });