From eaea22efb8f5ef260e7c548ebc9e69e8b9161c8e Mon Sep 17 00:00:00 2001 From: TongZ <471205975@qq.com> Date: Wed, 27 Jul 2022 00:03:17 +0800 Subject: [PATCH 1/7] feat(eslint-plugin): [no-use-before-define] add allowNamedExports option --- .../src/rules/no-use-before-define.ts | 18 ++ .../tests/rules/no-use-before-define.test.ts | 175 ++++++++++++++++++ 2 files changed, 193 insertions(+) diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index 3a5b51938b7..82ab9a807f2 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -15,6 +15,7 @@ function parseOptions(options: string | Config | null): Required { let variables = true; let typedefs = true; let ignoreTypeReferences = true; + let allowNamedExports = false; if (typeof options === 'string') { functions = options !== 'nofunc'; @@ -25,6 +26,7 @@ function parseOptions(options: string | Config | null): Required { variables = options.variables !== false; typedefs = options.typedefs !== false; ignoreTypeReferences = options.ignoreTypeReferences !== false; + allowNamedExports = options.allowNamedExports !== true; } return { @@ -34,6 +36,7 @@ function parseOptions(options: string | Config | null): Required { variables, typedefs, ignoreTypeReferences, + allowNamedExports, }; } @@ -90,6 +93,17 @@ function isOuterVariable( ); } +/** + * Checks whether or not a given reference is a export reference. + */ +function isAllowNamedExports(reference: TSESLint.Scope.Reference): boolean { + const { identifier } = reference; + return ( + identifier.parent?.type === 'ExportSpecifier' && + identifier.parent?.local === identifier + ); +} + /** * Recursively checks whether or not a given reference has a type query declaration among it's parents */ @@ -218,6 +232,7 @@ interface Config { variables?: boolean; typedefs?: boolean; ignoreTypeReferences?: boolean; + allowNamedExports?: boolean; } type Options = ['nofunc' | Config]; type MessageIds = 'noUseBeforeDefine'; @@ -281,6 +296,9 @@ export default util.createRule({ if (options.ignoreTypeReferences && isTypeReference(reference)) { return false; } + if (options.allowNamedExports && isAllowNamedExports(reference)) { + return false; + } if (isFunction(variable)) { return options.functions; } diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts index 32924973a5c..52f98226691 100644 --- a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts +++ b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts @@ -326,6 +326,39 @@ enum Foo { `, options: [{ enums: false }], }, + + // "allowNamedExports" option + { + code: 'export { a }; const a = 1;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + { + code: 'export { a as b }; const a = 1;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + { + code: 'export { a, b }; let a, b;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + { + code: 'export { a }; var a;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + { + code: 'export { f }; function f() {}', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + { + code: 'export { C }; class C {}', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + // https://github.com/typescript-eslint/typescript-eslint/issues/2502 { code: ` @@ -1094,6 +1127,148 @@ enum Foo { }, ], }, + // "allowNamedExports" option + { + code: 'export { a }; const a = 1;', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export { a }; const a = 1;', + options: [{}], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export { a }; const a = 1;', + options: [{ allowNamedExports: false }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export { a }; const a = 1;', + options: ['nofunc'], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export { a as b }; const a = 1;', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export { a, b }; let a, b;', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + { + messageId: 'noUseBeforeDefine', + data: { name: 'b' }, + }, + ], + }, + { + code: 'export { a }; var a;', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export { f }; function f() {}', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'f' }, + }, + ], + }, + { + code: 'export { C }; class C {}', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'C' }, + }, + ], + }, + { + code: 'export const foo = a; const a = 1;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export default a; const a = 1;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export function foo() { return a; }; const a = 1;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + { + code: 'export class C { foo() { return a; } }; const a = 1;', + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, { code: ` f(); From 65ec06f6fe5c630536da89e8a52e2b55cad6a93e Mon Sep 17 00:00:00 2001 From: TongZ <471205975@qq.com> Date: Wed, 27 Jul 2022 01:16:09 +0800 Subject: [PATCH 2/7] chore: tmp commit --- .../src/rules/no-use-before-define.ts | 46 +++- .../rules/no-use-before-define-test.test.ts | 252 ++++++++++++++++++ .../tests/rules/no-use-before-define.test.ts | 101 +++++-- 3 files changed, 368 insertions(+), 31 deletions(-) create mode 100644 packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index 82ab9a807f2..157d2c1b29e 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -26,7 +26,7 @@ function parseOptions(options: string | Config | null): Required { variables = options.variables !== false; typedefs = options.typedefs !== false; ignoreTypeReferences = options.ignoreTypeReferences !== false; - allowNamedExports = options.allowNamedExports !== true; + allowNamedExports = options.allowNamedExports !== false; } return { @@ -99,7 +99,8 @@ function isOuterVariable( function isAllowNamedExports(reference: TSESLint.Scope.Reference): boolean { const { identifier } = reference; return ( - identifier.parent?.type === 'ExportSpecifier' && + (identifier.parent?.type === AST_NODE_TYPES.ExportSpecifier || + identifier.parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration) && identifier.parent?.local === identifier ); } @@ -264,6 +265,7 @@ export default util.createRule({ variables: { type: 'boolean' }, typedefs: { type: 'boolean' }, ignoreTypeReferences: { type: 'boolean' }, + allowNamedExports: { type: 'boolean' }, }, additionalProperties: false, }, @@ -279,6 +281,7 @@ export default util.createRule({ variables: true, typedefs: true, ignoreTypeReferences: true, + allowNamedExports: false, }, ], create(context, optionsWithDefault) { @@ -296,9 +299,6 @@ export default util.createRule({ if (options.ignoreTypeReferences && isTypeReference(reference)) { return false; } - if (options.allowNamedExports && isAllowNamedExports(reference)) { - return false; - } if (isFunction(variable)) { return options.functions; } @@ -325,6 +325,22 @@ export default util.createRule({ scope.references.forEach(reference => { const variable = reference.resolved; + const report = (): void => + context.report({ + node: reference.identifier, + messageId: 'noUseBeforeDefine', + data: { + name: reference.identifier.name, + }, + }); + + // If "allowNamedExports" is false, check it first to avoid variable is null + + if (!options.allowNamedExports && isAllowNamedExports(reference)) { + report(); + return; + } + // Skips when the reference is: // - initializations. // - referring to an undefined variable. @@ -341,17 +357,23 @@ export default util.createRule({ isClassRefInClassDecorator(variable, reference) || reference.from.type === TSESLint.Scope.ScopeType.functionType ) { + console.log([ + reference.init, + !variable, + reference.identifier.parent?.type, + // variable.identifiers.length === 0, + // (variable.identifiers[0].range[1] <= reference.identifier.range[1] && + // !isInInitializer(variable, reference)), + // !isForbidden(variable, reference), + // isClassRefInClassDecorator(variable, reference), + // reference.from.type === TSESLint.Scope.ScopeType.functionType + ]); + return; } // Reports. - context.report({ - node: reference.identifier, - messageId: 'noUseBeforeDefine', - data: { - name: reference.identifier.name, - }, - }); + report(); }); scope.childScopes.forEach(findVariablesInScope); diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts new file mode 100644 index 00000000000..b0359119a41 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts @@ -0,0 +1,252 @@ +import rule from '../../src/rules/no-use-before-define'; +import { RuleTester } from '../RuleTester'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +const parserOptions = { ecmaVersion: 6 as const }; + +ruleTester.run('no-use-before-define', rule, { + valid: [ + // "allowNamedExports" option + { + code: ` +export { a }; +const a = 1; + `, + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, + // { + // code: ` + // export { a as b }; + // const a = 1; + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // }, + // { + // code: ` + // export { a, b }; + // let a, b; + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // }, + // { + // code: ` + // export { a }; + // var a; + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // }, + // { + // code: ` + // export { f }; + // function f() {} + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // }, + // { + // code: ` + // export { C }; + // class C {} + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // }, + ], + invalid: [ + // "allowNamedExports" option + // { + // code: ` + // export { a }; + // const a = 1; + // `, + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export { a }; + // const a = 1; + // `, + // options: [{}], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export { a }; + // const a = 1; + // `, + // options: [{ allowNamedExports: false }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export { a }; + // const a = 1; + // `, + // options: ['nofunc'], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export { a as b }; + // const a = 1; + // `, + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export { a, b }; + // let a, b; + // `, + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'b' }, + // }, + // ], + // }, + // { + // code: ` + // export { a }; + // var a; + // `, + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export { f }; + // function f() {} + // `, + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'f' }, + // }, + // ], + // }, + // { + // code: ` + // export { C }; + // class C {} + // `, + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'C' }, + // }, + // ], + // }, + // { + // code: ` + // export const foo = a; + // const a = 1; + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + { + code: ` +export default a; +const a = 1; + `, + options: [{ allowNamedExports: true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'a' }, + }, + ], + }, + // { + // code: ` + // export function foo() { + // return a; + // } + // const a = 1; + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + // { + // code: ` + // export class C { + // foo() { + // return a; + // } + // } + // const a = 1; + // `, + // options: [{ allowNamedExports: true }], + // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + // errors: [ + // { + // messageId: 'noUseBeforeDefine', + // data: { name: 'a' }, + // }, + // ], + // }, + ], +}); diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts index 52f98226691..8790723dbc3 100644 --- a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts +++ b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts @@ -329,32 +329,50 @@ enum Foo { // "allowNamedExports" option { - code: 'export { a }; const a = 1;', + code: ` +export { a }; +const a = 1; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { - code: 'export { a as b }; const a = 1;', + code: ` +export { a as b }; +const a = 1; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { - code: 'export { a, b }; let a, b;', + code: ` +export { a, b }; +let a, b; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { - code: 'export { a }; var a;', + code: ` +export { a }; +var a; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { - code: 'export { f }; function f() {}', + code: ` +export { f }; +function f() {} + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { - code: 'export { C }; class C {}', + code: ` +export { C }; +class C {} + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, @@ -1129,7 +1147,10 @@ enum Foo { }, // "allowNamedExports" option { - code: 'export { a }; const a = 1;', + code: ` +export { a }; +const a = 1; + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { @@ -1139,7 +1160,10 @@ enum Foo { ], }, { - code: 'export { a }; const a = 1;', + code: ` +export { a }; +const a = 1; + `, options: [{}], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ @@ -1150,7 +1174,10 @@ enum Foo { ], }, { - code: 'export { a }; const a = 1;', + code: ` +export { a }; +const a = 1; + `, options: [{ allowNamedExports: false }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ @@ -1161,7 +1188,10 @@ enum Foo { ], }, { - code: 'export { a }; const a = 1;', + code: ` +export { a }; +const a = 1; + `, options: ['nofunc'], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ @@ -1172,7 +1202,10 @@ enum Foo { ], }, { - code: 'export { a as b }; const a = 1;', + code: ` +export { a as b }; +const a = 1; + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { @@ -1182,7 +1215,10 @@ enum Foo { ], }, { - code: 'export { a, b }; let a, b;', + code: ` +export { a, b }; +let a, b; + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { @@ -1196,7 +1232,10 @@ enum Foo { ], }, { - code: 'export { a }; var a;', + code: ` +export { a }; +var a; + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { @@ -1206,7 +1245,10 @@ enum Foo { ], }, { - code: 'export { f }; function f() {}', + code: ` +export { f }; +function f() {} + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { @@ -1216,7 +1258,10 @@ enum Foo { ], }, { - code: 'export { C }; class C {}', + code: ` +export { C }; +class C {} + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { @@ -1226,7 +1271,10 @@ enum Foo { ], }, { - code: 'export const foo = a; const a = 1;', + code: ` +export const foo = a; +const a = 1; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ @@ -1237,7 +1285,10 @@ enum Foo { ], }, { - code: 'export default a; const a = 1;', + code: ` +export default a; +const a = 1; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ @@ -1248,7 +1299,12 @@ enum Foo { ], }, { - code: 'export function foo() { return a; }; const a = 1;', + code: ` +export function foo() { + return a; +} +const a = 1; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ @@ -1259,7 +1315,14 @@ enum Foo { ], }, { - code: 'export class C { foo() { return a; } }; const a = 1;', + code: ` +export class C { + foo() { + return a; + } +} +const a = 1; + `, options: [{ allowNamedExports: true }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ From 60a32c68b2ae063911ae3e957ccf9d84b8bd8152 Mon Sep 17 00:00:00 2001 From: tongz Date: Wed, 27 Jul 2022 18:03:07 +0800 Subject: [PATCH 3/7] chore(eslint-plugin): tmp commit --- .../src/rules/no-use-before-define.ts | 63 ++++++++++++------- .../rules/no-use-before-define-test.test.ts | 2 +- .../tests/rules/no-use-before-define.test.ts | 14 ----- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index 157d2c1b29e..f0e236d222a 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -96,11 +96,10 @@ function isOuterVariable( /** * Checks whether or not a given reference is a export reference. */ -function isAllowNamedExports(reference: TSESLint.Scope.Reference): boolean { +function isNamedExports(reference: TSESLint.Scope.Reference): boolean { const { identifier } = reference; return ( - (identifier.parent?.type === AST_NODE_TYPES.ExportSpecifier || - identifier.parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration) && + identifier.parent?.type === AST_NODE_TYPES.ExportSpecifier && identifier.parent?.local === identifier ); } @@ -297,23 +296,30 @@ export default util.createRule({ reference: TSESLint.Scope.Reference, ): boolean { if (options.ignoreTypeReferences && isTypeReference(reference)) { + // console.log(1); return false; } if (isFunction(variable)) { + // console.log(2); return options.functions; } if (isOuterClass(variable, reference)) { + // console.log(3); return options.classes; } if (isOuterVariable(variable, reference)) { + // console.log(4); return options.variables; } if (isOuterEnum(variable, reference)) { + // console.log(5); return options.enums; } if (isTypedef(variable)) { + // console.log(6); return options.typedefs; } + // console.log(7); return true; } @@ -334,22 +340,24 @@ export default util.createRule({ }, }); - // If "allowNamedExports" is false, check it first to avoid variable is null - - if (!options.allowNamedExports && isAllowNamedExports(reference)) { - report(); - return; - } - // Skips when the reference is: // - initializations. // - referring to an undefined variable. // - referring to a global environment variable (there're no identifiers). // - located preceded by the variable (except in initializers). // - allowed by options. + if (reference.init) { + return; + } + // If "allowNamedExports" is false, check it first to avoid variable is null + if (!options.allowNamedExports && isNamedExports(reference)) { + report(); + return; + } + if (!variable) { + return; + } if ( - reference.init || - !variable || variable.identifiers.length === 0 || (variable.identifiers[0].range[1] <= reference.identifier.range[1] && !isInInitializer(variable, reference)) || @@ -357,17 +365,26 @@ export default util.createRule({ isClassRefInClassDecorator(variable, reference) || reference.from.type === TSESLint.Scope.ScopeType.functionType ) { - console.log([ - reference.init, - !variable, - reference.identifier.parent?.type, - // variable.identifiers.length === 0, - // (variable.identifiers[0].range[1] <= reference.identifier.range[1] && - // !isInInitializer(variable, reference)), - // !isForbidden(variable, reference), - // isClassRefInClassDecorator(variable, reference), - // reference.from.type === TSESLint.Scope.ScopeType.functionType - ]); + // console.log([ + // !isForbidden(variable, reference), + // reference.init, + // !variable || variable.identifiers.length === 0 || (variable.identifiers[0].range[1] <= reference.identifier.range[1] && + // !isInInitializer(variable, reference)), + // !variable || + // variable.identifiers.length === 0 || + // (variable.identifiers[0].range[1] <= reference.identifier.range[1] && + // !isInInitializer(variable, reference)) || + // !isForbidden(variable, reference) || + // isClassRefInClassDecorator(variable, reference) || + // reference.from.type === TSESLint.Scope.ScopeType.functionType, + // reference.identifier.parent?.type, + // // variable.identifiers.length === 0, + // // (variable.identifiers[0].range[1] <= reference.identifier.range[1] && + // // !isInInitializer(variable, reference)), + // // !isForbidden(variable, reference), + // // isClassRefInClassDecorator(variable, reference), + // // reference.from.type === TSESLint.Scope.ScopeType.functionType + // ]); return; } diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts index b0359119a41..1479727208c 100644 --- a/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts +++ b/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts @@ -205,7 +205,7 @@ const a = 1; export default a; const a = 1; `, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true, ignoreTypeReferences: false }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, errors: [ { diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts index 8790723dbc3..84f4040f4f2 100644 --- a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts +++ b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts @@ -1273,20 +1273,6 @@ class C {} { code: ` export const foo = a; -const a = 1; - `, - options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - errors: [ - { - messageId: 'noUseBeforeDefine', - data: { name: 'a' }, - }, - ], - }, - { - code: ` -export default a; const a = 1; `, options: [{ allowNamedExports: true }], From 973fd166984231397dd601cd01592ee5b400774a Mon Sep 17 00:00:00 2001 From: tongz Date: Thu, 28 Jul 2022 18:51:33 +0800 Subject: [PATCH 4/7] feat(eslint-plugin): add allowNamedExports option --- .../src/rules/no-use-before-define.ts | 42 +-- .../rules/no-use-before-define-test.test.ts | 252 ------------------ .../tests/rules/no-use-before-define.test.ts | 141 ++++++++-- 3 files changed, 133 insertions(+), 302 deletions(-) delete mode 100644 packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index f0e236d222a..ebd4ef5a4d0 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -296,30 +296,23 @@ export default util.createRule({ reference: TSESLint.Scope.Reference, ): boolean { if (options.ignoreTypeReferences && isTypeReference(reference)) { - // console.log(1); return false; } if (isFunction(variable)) { - // console.log(2); return options.functions; } if (isOuterClass(variable, reference)) { - // console.log(3); return options.classes; } if (isOuterVariable(variable, reference)) { - // console.log(4); return options.variables; } if (isOuterEnum(variable, reference)) { - // console.log(5); return options.enums; } if (isTypedef(variable)) { - // console.log(6); return options.typedefs; } - // console.log(7); return true; } @@ -349,14 +342,22 @@ export default util.createRule({ if (reference.init) { return; } - // If "allowNamedExports" is false, check it first to avoid variable is null + if (!options.allowNamedExports && isNamedExports(reference)) { - report(); - return; + if ( + !variable || + variable.identifiers[0].range[1] > reference.identifier.range[1] || + isInInitializer(variable, reference) + ) { + report(); + return; + } } + if (!variable) { return; } + if ( variable.identifiers.length === 0 || (variable.identifiers[0].range[1] <= reference.identifier.range[1] && @@ -365,27 +366,6 @@ export default util.createRule({ isClassRefInClassDecorator(variable, reference) || reference.from.type === TSESLint.Scope.ScopeType.functionType ) { - // console.log([ - // !isForbidden(variable, reference), - // reference.init, - // !variable || variable.identifiers.length === 0 || (variable.identifiers[0].range[1] <= reference.identifier.range[1] && - // !isInInitializer(variable, reference)), - // !variable || - // variable.identifiers.length === 0 || - // (variable.identifiers[0].range[1] <= reference.identifier.range[1] && - // !isInInitializer(variable, reference)) || - // !isForbidden(variable, reference) || - // isClassRefInClassDecorator(variable, reference) || - // reference.from.type === TSESLint.Scope.ScopeType.functionType, - // reference.identifier.parent?.type, - // // variable.identifiers.length === 0, - // // (variable.identifiers[0].range[1] <= reference.identifier.range[1] && - // // !isInInitializer(variable, reference)), - // // !isForbidden(variable, reference), - // // isClassRefInClassDecorator(variable, reference), - // // reference.from.type === TSESLint.Scope.ScopeType.functionType - // ]); - return; } diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts deleted file mode 100644 index 1479727208c..00000000000 --- a/packages/eslint-plugin/tests/rules/no-use-before-define-test.test.ts +++ /dev/null @@ -1,252 +0,0 @@ -import rule from '../../src/rules/no-use-before-define'; -import { RuleTester } from '../RuleTester'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser', -}); - -const parserOptions = { ecmaVersion: 6 as const }; - -ruleTester.run('no-use-before-define', rule, { - valid: [ - // "allowNamedExports" option - { - code: ` -export { a }; -const a = 1; - `, - options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - }, - // { - // code: ` - // export { a as b }; - // const a = 1; - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // }, - // { - // code: ` - // export { a, b }; - // let a, b; - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // }, - // { - // code: ` - // export { a }; - // var a; - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // }, - // { - // code: ` - // export { f }; - // function f() {} - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // }, - // { - // code: ` - // export { C }; - // class C {} - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // }, - ], - invalid: [ - // "allowNamedExports" option - // { - // code: ` - // export { a }; - // const a = 1; - // `, - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export { a }; - // const a = 1; - // `, - // options: [{}], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export { a }; - // const a = 1; - // `, - // options: [{ allowNamedExports: false }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export { a }; - // const a = 1; - // `, - // options: ['nofunc'], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export { a as b }; - // const a = 1; - // `, - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export { a, b }; - // let a, b; - // `, - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'b' }, - // }, - // ], - // }, - // { - // code: ` - // export { a }; - // var a; - // `, - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export { f }; - // function f() {} - // `, - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'f' }, - // }, - // ], - // }, - // { - // code: ` - // export { C }; - // class C {} - // `, - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'C' }, - // }, - // ], - // }, - // { - // code: ` - // export const foo = a; - // const a = 1; - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - { - code: ` -export default a; -const a = 1; - `, - options: [{ allowNamedExports: true, ignoreTypeReferences: false }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - errors: [ - { - messageId: 'noUseBeforeDefine', - data: { name: 'a' }, - }, - ], - }, - // { - // code: ` - // export function foo() { - // return a; - // } - // const a = 1; - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - // { - // code: ` - // export class C { - // foo() { - // return a; - // } - // } - // const a = 1; - // `, - // options: [{ allowNamedExports: true }], - // parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - // errors: [ - // { - // messageId: 'noUseBeforeDefine', - // data: { name: 'a' }, - // }, - // ], - // }, - ], -}); diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts index 84f4040f4f2..19bdf5ee016 100644 --- a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts +++ b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts @@ -334,7 +334,7 @@ export { a }; const a = 1; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, }, { code: ` @@ -342,7 +342,7 @@ export { a as b }; const a = 1; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, }, { code: ` @@ -350,7 +350,7 @@ export { a, b }; let a, b; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, }, { code: ` @@ -358,7 +358,7 @@ export { a }; var a; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, }, { code: ` @@ -366,7 +366,7 @@ export { f }; function f() {} `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, }, { code: ` @@ -374,9 +374,44 @@ export { C }; class C {} `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + }, + { + code: ` +export { Foo }; + +enum Foo { + BAR, +} + `, + options: [{ allowNamedExports: true }], + parserOptions, }, + { + code: ` +export { Foo }; +namespace Foo { + export let bar = () => console.log('bar'); +} + `, + options: [{ allowNamedExports: true }], + parserOptions, + }, + { + code: ` +export { Foo, baz }; + +enum Foo { + BAR, +} + +let baz: Enum; +enum Enum {} + `, + options: [{ ignoreTypeReferences: true, allowNamedExports: true }], + parserOptions, + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2502 { code: ` @@ -1145,13 +1180,14 @@ enum Foo { }, ], }, - // "allowNamedExports" option + // "allowNamedExports" { code: ` export { a }; const a = 1; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1165,7 +1201,8 @@ export { a }; const a = 1; `, options: [{}], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1179,7 +1216,8 @@ export { a }; const a = 1; `, options: [{ allowNamedExports: false }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1193,7 +1231,8 @@ export { a }; const a = 1; `, options: ['nofunc'], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1206,7 +1245,8 @@ const a = 1; export { a as b }; const a = 1; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1219,7 +1259,8 @@ const a = 1; export { a, b }; let a, b; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1236,7 +1277,8 @@ let a, b; export { a }; var a; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1249,7 +1291,8 @@ var a; export { f }; function f() {} `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1262,7 +1305,8 @@ function f() {} export { C }; class C {} `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1276,7 +1320,8 @@ export const foo = a; const a = 1; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1292,7 +1337,8 @@ export function foo() { const a = 1; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1310,7 +1356,8 @@ export class C { const a = 1; `, options: [{ allowNamedExports: true }], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + parserOptions, + errors: [ { messageId: 'noUseBeforeDefine', @@ -1320,6 +1367,62 @@ const a = 1; }, { code: ` +export { Foo }; + +enum Foo { + BAR, +} + `, + parserOptions, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'Foo' }, + }, + ], + }, + { + code: ` +export { Foo }; + +namespace Foo { + export let bar = () => console.log('bar'); +} + `, + parserOptions, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'Foo' }, + }, + ], + }, + { + code: ` +export { Foo, baz }; + +enum Foo { + BAR, +} + +let baz: Enum; +enum Enum {} + `, + options: [{ ignoreTypeReferences: true, allowNamedExports: false }], + parserOptions, + errors: [ + { + messageId: 'noUseBeforeDefine', + data: { name: 'Foo' }, + }, + { + messageId: 'noUseBeforeDefine', + data: { name: 'baz' }, + }, + ], + }, + { + code: ` f(); function f() {} `, From b71d1a77f95136af1e4d8d46213531e19af19af0 Mon Sep 17 00:00:00 2001 From: tongz Date: Fri, 29 Jul 2022 09:30:30 +0800 Subject: [PATCH 5/7] chore(eslint-plugin): lint --- .../tests/rules/no-use-before-define.test.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts index 19bdf5ee016..676704fb279 100644 --- a/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts +++ b/packages/eslint-plugin/tests/rules/no-use-before-define.test.ts @@ -409,7 +409,7 @@ enum Foo { let baz: Enum; enum Enum {} `, - options: [{ ignoreTypeReferences: true, allowNamedExports: true }], + options: [{ allowNamedExports: true }], parserOptions, }, // https://github.com/typescript-eslint/typescript-eslint/issues/2502 @@ -1180,14 +1180,13 @@ enum Foo { }, ], }, - // "allowNamedExports" + // "allowNamedExports" option { code: ` export { a }; const a = 1; `, parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1202,7 +1201,6 @@ const a = 1; `, options: [{}], parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1217,7 +1215,6 @@ const a = 1; `, options: [{ allowNamedExports: false }], parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1232,7 +1229,6 @@ const a = 1; `, options: ['nofunc'], parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1246,7 +1242,6 @@ export { a as b }; const a = 1; `, parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1260,7 +1255,6 @@ export { a, b }; let a, b; `, parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1278,7 +1272,6 @@ export { a }; var a; `, parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1292,7 +1285,6 @@ export { f }; function f() {} `, parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1306,7 +1298,6 @@ export { C }; class C {} `, parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', @@ -1321,7 +1312,6 @@ const a = 1; `, options: [{ allowNamedExports: true }], parserOptions, - errors: [ { messageId: 'noUseBeforeDefine', From 4045bc9cd1313a85dbad78fddaa08a22ddb27bce Mon Sep 17 00:00:00 2001 From: tongz Date: Fri, 29 Jul 2022 09:54:03 +0800 Subject: [PATCH 6/7] chore(eslint-plugin): human readable refactor --- .../src/rules/no-use-before-define.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index ebd4ef5a4d0..f813f9ad6fe 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -317,6 +317,16 @@ export default util.createRule({ return true; } + function isDefinedBeforeUse( + variable: TSESLint.Scope.Variable, + reference: TSESLint.Scope.Reference, + ): boolean { + return ( + variable.identifiers[0].range[1] <= reference.identifier.range[1] && + !isInInitializer(variable, reference) + ); + } + /** * Finds and validates all variables in a given scope. */ @@ -324,7 +334,7 @@ export default util.createRule({ scope.references.forEach(reference => { const variable = reference.resolved; - const report = (): void => + function report(): void { context.report({ node: reference.identifier, messageId: 'noUseBeforeDefine', @@ -332,6 +342,7 @@ export default util.createRule({ name: reference.identifier.name, }, }); + } // Skips when the reference is: // - initializations. @@ -344,14 +355,10 @@ export default util.createRule({ } if (!options.allowNamedExports && isNamedExports(reference)) { - if ( - !variable || - variable.identifiers[0].range[1] > reference.identifier.range[1] || - isInInitializer(variable, reference) - ) { + if (!variable || !isDefinedBeforeUse(variable, reference)) { report(); - return; } + return; } if (!variable) { @@ -360,8 +367,7 @@ export default util.createRule({ if ( variable.identifiers.length === 0 || - (variable.identifiers[0].range[1] <= reference.identifier.range[1] && - !isInInitializer(variable, reference)) || + isDefinedBeforeUse(variable, reference) || !isForbidden(variable, reference) || isClassRefInClassDecorator(variable, reference) || reference.from.type === TSESLint.Scope.ScopeType.functionType From ead858d69070168d56e0d0dbc5a112b025f90721 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 29 Jul 2022 00:04:44 -0400 Subject: [PATCH 7/7] Update packages/eslint-plugin/src/rules/no-use-before-define.ts --- packages/eslint-plugin/src/rules/no-use-before-define.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index f813f9ad6fe..f9e397283ce 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -100,7 +100,7 @@ function isNamedExports(reference: TSESLint.Scope.Reference): boolean { const { identifier } = reference; return ( identifier.parent?.type === AST_NODE_TYPES.ExportSpecifier && - identifier.parent?.local === identifier + identifier.parent.local === identifier ); }