Skip to content

Commit 6edd911

Browse files
a-tarasyukbradzacher
authored andcommitted
feat(eslint-plugin): [no-use-before-define] opt to ignore enum (#1242)
1 parent 2b16019 commit 6edd911

File tree

3 files changed

+201
-25
lines changed

3 files changed

+201
-25
lines changed

packages/eslint-plugin/docs/rules/no-use-before-define.md

+42
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ let myVar: StringOrNumber;
8383
Otherwise, ignores those references if the declaration is in upper function scopes.
8484
Class declarations are not hoisted, so it might be danger.
8585
Default is `true`.
86+
- `enums` (`boolean`) -
87+
The flag which shows whether or not this rule checks enum declarations of upper scopes.
88+
If this is `true`, this rule warns every reference to a enum before the enum declaration.
89+
Otherwise, ignores those references.
90+
Default is `true`.
8691
- `variables` (`boolean`) -
8792
This flag determines whether or not the rule checks variable declarations in upper scopes.
8893
If this is `true`, the rule warns every reference to a variable before the variable declaration.
@@ -134,6 +139,43 @@ function foo() {
134139
class A {}
135140
```
136141

142+
### `enums`
143+
144+
Examples of **incorrect** code for the `{ "enums": true }` option:
145+
146+
```ts
147+
/*eslint no-use-before-define: ["error", { "enums": true }]*/
148+
149+
function foo() {
150+
return Foo.FOO;
151+
}
152+
153+
class Test {
154+
foo() {
155+
return Foo.FOO;
156+
}
157+
}
158+
159+
enum Foo {
160+
FOO,
161+
BAR,
162+
}
163+
```
164+
165+
Examples of **correct** code for the `{ "enums": false }` option:
166+
167+
```ts
168+
/*eslint no-use-before-define: ["error", { "enums": false }]*/
169+
170+
function foo() {
171+
return Foo.FOO;
172+
}
173+
174+
enum Foo {
175+
FOO,
176+
}
177+
```
178+
137179
### `variables`
138180

139181
Examples of **incorrect** code for the `{ "variables": false }` option:

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

+48-25
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFun
1313
function parseOptions(options: string | Config | null): Required<Config> {
1414
let functions = true;
1515
let classes = true;
16+
let enums = true;
1617
let variables = true;
1718
let typedefs = true;
1819

@@ -21,11 +22,12 @@ function parseOptions(options: string | Config | null): Required<Config> {
2122
} else if (typeof options === 'object' && options !== null) {
2223
functions = options.functions !== false;
2324
classes = options.classes !== false;
25+
enums = options.enums !== false;
2426
variables = options.variables !== false;
2527
typedefs = options.typedefs !== false;
2628
}
2729

28-
return { functions, classes, variables, typedefs };
30+
return { functions, classes, enums, variables, typedefs };
2931
}
3032

3133
/**
@@ -35,6 +37,23 @@ function isTopLevelScope(scope: TSESLint.Scope.Scope): boolean {
3537
return scope.type === 'module' || scope.type === 'global';
3638
}
3739

40+
/**
41+
* Checks whether or not a given variable declaration in an upper scope.
42+
*/
43+
function isOuterScope(
44+
variable: TSESLint.Scope.Variable,
45+
reference: TSESLint.Scope.Reference,
46+
): boolean {
47+
if (variable.scope.variableScope === reference.from.variableScope) {
48+
// allow the same scope only if it's the top level global/module scope
49+
if (!isTopLevelScope(variable.scope.variableScope)) {
50+
return false;
51+
}
52+
}
53+
54+
return true;
55+
}
56+
3857
/**
3958
* Checks whether or not a given variable is a function declaration.
4059
*/
@@ -43,24 +62,30 @@ function isFunction(variable: TSESLint.Scope.Variable): boolean {
4362
}
4463

4564
/**
46-
* Checks whether or not a given variable is a class declaration in an upper function scope.
65+
* Checks whether or not a given variable is a enum declaration in an upper function scope.
4766
*/
48-
function isOuterClass(
67+
function isOuterEnum(
4968
variable: TSESLint.Scope.Variable,
5069
reference: TSESLint.Scope.Reference,
5170
): boolean {
52-
if (variable.defs[0].type !== 'ClassName') {
53-
return false;
54-
}
71+
const node = variable.defs[0].node as TSESTree.Node;
5572

56-
if (variable.scope.variableScope === reference.from.variableScope) {
57-
// allow the same scope only if it's the top level global/module scope
58-
if (!isTopLevelScope(variable.scope.variableScope)) {
59-
return false;
60-
}
61-
}
73+
return (
74+
node.type === AST_NODE_TYPES.TSEnumDeclaration &&
75+
isOuterScope(variable, reference)
76+
);
77+
}
6278

63-
return true;
79+
/**
80+
* Checks whether or not a given variable is a class declaration in an upper function scope.
81+
*/
82+
function isOuterClass(
83+
variable: TSESLint.Scope.Variable,
84+
reference: TSESLint.Scope.Reference,
85+
): boolean {
86+
return (
87+
variable.defs[0].type === 'ClassName' && isOuterScope(variable, reference)
88+
);
6489
}
6590

6691
/**
@@ -70,18 +95,9 @@ function isOuterVariable(
7095
variable: TSESLint.Scope.Variable,
7196
reference: TSESLint.Scope.Reference,
7297
): boolean {
73-
if (variable.defs[0].type !== 'Variable') {
74-
return false;
75-
}
76-
77-
if (variable.scope.variableScope === reference.from.variableScope) {
78-
// allow the same scope only if it's the top level global/module scope
79-
if (!isTopLevelScope(variable.scope.variableScope)) {
80-
return false;
81-
}
82-
}
83-
84-
return true;
98+
return (
99+
variable.defs[0].type === 'Variable' && isOuterScope(variable, reference)
100+
);
85101
}
86102

87103
/**
@@ -147,6 +163,7 @@ function isInInitializer(
147163
interface Config {
148164
functions?: boolean;
149165
classes?: boolean;
166+
enums?: boolean;
150167
variables?: boolean;
151168
typedefs?: boolean;
152169
}
@@ -176,6 +193,7 @@ export default util.createRule<Options, MessageIds>({
176193
properties: {
177194
functions: { type: 'boolean' },
178195
classes: { type: 'boolean' },
196+
enums: { type: 'boolean' },
179197
variables: { type: 'boolean' },
180198
typedefs: { type: 'boolean' },
181199
},
@@ -189,6 +207,7 @@ export default util.createRule<Options, MessageIds>({
189207
{
190208
functions: true,
191209
classes: true,
210+
enums: true,
192211
variables: true,
193212
typedefs: true,
194213
},
@@ -214,6 +233,10 @@ export default util.createRule<Options, MessageIds>({
214233
if (isOuterVariable(variable, reference)) {
215234
return !!options.variables;
216235
}
236+
if (isOuterEnum(variable, reference)) {
237+
return !!options.enums;
238+
}
239+
217240
return true;
218241
}
219242

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

+111
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,54 @@ interface Foo {
229229
}
230230
const bar = 'blah'
231231
`,
232+
{
233+
code: `
234+
function foo(): Foo {
235+
return Foo.FOO;
236+
}
237+
238+
enum Foo {
239+
FOO,
240+
}
241+
`,
242+
options: [
243+
{
244+
enums: false,
245+
},
246+
],
247+
},
248+
{
249+
code: `
250+
let foo: Foo;
251+
252+
enum Foo {
253+
FOO,
254+
}
255+
`,
256+
options: [
257+
{
258+
enums: false,
259+
},
260+
],
261+
},
262+
{
263+
code: `
264+
class Test {
265+
foo(args: Foo): Foo {
266+
return Foo.FOO;
267+
}
268+
}
269+
270+
enum Foo {
271+
FOO,
272+
}
273+
`,
274+
options: [
275+
{
276+
enums: false,
277+
},
278+
],
279+
},
232280
],
233281
invalid: [
234282
{
@@ -840,5 +888,68 @@ var bar;
840888
},
841889
],
842890
},
891+
{
892+
code: `
893+
class Test {
894+
foo(args: Foo): Foo {
895+
return Foo.FOO;
896+
}
897+
}
898+
899+
enum Foo {
900+
FOO,
901+
}
902+
`,
903+
options: [
904+
{
905+
enums: true,
906+
},
907+
],
908+
errors: [
909+
{
910+
messageId: 'noUseBeforeDefine',
911+
},
912+
],
913+
},
914+
{
915+
code: `
916+
function foo(): Foo {
917+
return Foo.FOO;
918+
}
919+
920+
enum Foo {
921+
FOO,
922+
}
923+
`,
924+
options: [
925+
{
926+
enums: true,
927+
},
928+
],
929+
errors: [
930+
{
931+
messageId: 'noUseBeforeDefine',
932+
},
933+
],
934+
},
935+
{
936+
code: `
937+
const foo = Foo.Foo;
938+
939+
enum Foo {
940+
FOO,
941+
}
942+
`,
943+
options: [
944+
{
945+
enums: true,
946+
},
947+
],
948+
errors: [
949+
{
950+
messageId: 'noUseBeforeDefine',
951+
},
952+
],
953+
},
843954
],
844955
});

0 commit comments

Comments
 (0)