Skip to content

Commit 050023a

Browse files
authored
fix(eslint-plugin): [no-use-before-define] allow class references if they're within a class decorator (typescript-eslint#2827)
Fixes typescript-eslint#2842
1 parent bf904ec commit 050023a

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

.cspell.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@
8989
"rulesets",
9090
"serializers",
9191
"superset",
92-
"transpiling",
9392
"thenables",
93+
"transpiled",
9494
"transpiles",
95+
"transpiling",
9596
"tsconfigs",
9697
"tsutils",
9798
"typedef",

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,37 @@ function isInRange(
133133
return !!node && node.range[0] <= location && location <= node.range[1];
134134
}
135135

136+
/**
137+
* Decorators are transpiled such that the decorator is placed after the class declaration
138+
* So it is considered safe
139+
*/
140+
function isClassRefInClassDecorator(
141+
variable: TSESLint.Scope.Variable,
142+
reference: TSESLint.Scope.Reference,
143+
): boolean {
144+
if (variable.defs[0].type !== 'ClassName') {
145+
return false;
146+
}
147+
148+
if (
149+
!variable.defs[0].node.decorators ||
150+
variable.defs[0].node.decorators.length === 0
151+
) {
152+
return false;
153+
}
154+
155+
for (const deco of variable.defs[0].node.decorators) {
156+
if (
157+
reference.identifier.range[0] >= deco.range[0] &&
158+
reference.identifier.range[1] <= deco.range[1]
159+
) {
160+
return true;
161+
}
162+
}
163+
164+
return false;
165+
}
166+
136167
/**
137168
* Checks whether or not a given reference is inside of the initializers of a given variable.
138169
*
@@ -292,6 +323,7 @@ export default util.createRule<Options, MessageIds>({
292323
(variable.identifiers[0].range[1] <= reference.identifier.range[1] &&
293324
!isInInitializer(variable, reference)) ||
294325
!isForbidden(variable, reference) ||
326+
isClassRefInClassDecorator(variable, reference) ||
295327
reference.from.type === TSESLint.Scope.ScopeType.functionType
296328
) {
297329
return;

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,40 @@ declare global {
394394
}
395395
}
396396
`,
397+
// https://github.com/typescript-eslint/typescript-eslint/issues/2824
398+
`
399+
@Directive({
400+
selector: '[rcCidrIpPattern]',
401+
providers: [
402+
{
403+
provide: NG_VALIDATORS,
404+
useExisting: CidrIpPatternDirective,
405+
multi: true,
406+
},
407+
],
408+
})
409+
export class CidrIpPatternDirective implements Validator {}
410+
`,
411+
{
412+
code: `
413+
@Directive({
414+
selector: '[rcCidrIpPattern]',
415+
providers: [
416+
{
417+
provide: NG_VALIDATORS,
418+
useExisting: CidrIpPatternDirective,
419+
multi: true,
420+
},
421+
],
422+
})
423+
export class CidrIpPatternDirective implements Validator {}
424+
`,
425+
options: [
426+
{
427+
classes: false,
428+
},
429+
],
430+
},
397431
],
398432
invalid: [
399433
{

0 commit comments

Comments
 (0)