Skip to content

Commit 8e44c78

Browse files
authored
fix(eslint-plugin): [no-use-before-define] correctly handle typeof type references (typescript-eslint#2623)
1 parent 03886d7 commit 8e44c78

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

packages/eslint-plugin/src/rules/no-use-before-define.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,37 @@ function isOuterVariable(
9292
);
9393
}
9494

95+
/**
96+
* Recursively checks whether or not a given reference has a type query declaration among it's parents
97+
*/
98+
function referenceContainsTypeQuery(node: TSESTree.Node): boolean {
99+
switch (node.type) {
100+
case AST_NODE_TYPES.TSTypeQuery:
101+
return true;
102+
103+
case AST_NODE_TYPES.TSQualifiedName:
104+
case AST_NODE_TYPES.Identifier:
105+
if (!node.parent) {
106+
return false;
107+
}
108+
return referenceContainsTypeQuery(node.parent);
109+
110+
default:
111+
// if we find a different node, there's no chance that we're in a TSTypeQuery
112+
return false;
113+
}
114+
}
115+
116+
/**
117+
* Checks whether or not a given reference is a type reference.
118+
*/
119+
function isTypeReference(reference: TSESLint.Scope.Reference): boolean {
120+
return (
121+
reference.isTypeReference ||
122+
referenceContainsTypeQuery(reference.identifier)
123+
);
124+
}
125+
95126
/**
96127
* Checks whether or not a given location is inside of the range of a given node.
97128
*/
@@ -219,7 +250,7 @@ export default util.createRule<Options, MessageIds>({
219250
variable: TSESLint.Scope.Variable,
220251
reference: TSESLint.Scope.Reference,
221252
): boolean {
222-
if (reference.isTypeReference && options.ignoreTypeReferences) {
253+
if (options.ignoreTypeReferences && isTypeReference(reference)) {
223254
return false;
224255
}
225256
if (isFunction(variable)) {

packages/eslint-plugin/tests/rules/no-use-before-define.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,43 @@ type Foo = string | number;
219219
`,
220220
options: [{ typedefs: false }],
221221
},
222+
// https://github.com/typescript-eslint/typescript-eslint/issues/2572
223+
{
224+
code: `
225+
interface Bar {
226+
type: typeof Foo;
227+
}
228+
229+
const Foo = 2;
230+
`,
231+
options: [{ ignoreTypeReferences: true }],
232+
},
233+
{
234+
code: `
235+
interface Bar {
236+
type: typeof Foo.FOO;
237+
}
238+
239+
class Foo {
240+
public static readonly FOO = '';
241+
}
242+
`,
243+
options: [{ ignoreTypeReferences: true }],
244+
},
245+
{
246+
code: `
247+
interface Bar {
248+
type: typeof Foo.Bar.Baz;
249+
}
222250
251+
const Foo = {
252+
Bar: {
253+
Baz: 1,
254+
},
255+
};
256+
`,
257+
options: [{ ignoreTypeReferences: true }],
258+
},
223259
// https://github.com/bradzacher/eslint-plugin-typescript/issues/141
224260
{
225261
code: `
@@ -875,6 +911,65 @@ for (var a of a) {
875911
],
876912
},
877913

914+
// "ignoreTypeReferences" option
915+
{
916+
code: `
917+
interface Bar {
918+
type: typeof Foo;
919+
}
920+
921+
const Foo = 2;
922+
`,
923+
options: [{ ignoreTypeReferences: false }],
924+
errors: [
925+
{
926+
messageId: 'noUseBeforeDefine',
927+
data: { name: 'Foo' },
928+
type: AST_NODE_TYPES.Identifier,
929+
},
930+
],
931+
},
932+
{
933+
code: `
934+
interface Bar {
935+
type: typeof Foo.FOO;
936+
}
937+
938+
class Foo {
939+
public static readonly FOO = '';
940+
}
941+
`,
942+
options: [{ ignoreTypeReferences: false }],
943+
errors: [
944+
{
945+
messageId: 'noUseBeforeDefine',
946+
data: { name: 'Foo' },
947+
type: AST_NODE_TYPES.Identifier,
948+
},
949+
],
950+
},
951+
{
952+
code: `
953+
interface Bar {
954+
type: typeof Foo.Bar.Baz;
955+
}
956+
957+
const Foo = {
958+
Bar: {
959+
Baz: 1,
960+
},
961+
};
962+
`,
963+
options: [{ ignoreTypeReferences: false }],
964+
errors: [
965+
{
966+
messageId: 'noUseBeforeDefine',
967+
data: { name: 'Foo' },
968+
type: AST_NODE_TYPES.Identifier,
969+
},
970+
],
971+
},
972+
878973
// "variables" option
879974
{
880975
code: `

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy