Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(eslint-plugin): add
prefer-enum-initializers
rule (#2326)
- Loading branch information
1 parent
3ef6bd5
commit 4f38ea3
Showing
6 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
packages/eslint-plugin/docs/rules/prefer-enum-initializers.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Prefer initializing each enums member value (`prefer-enum-initializers`) | ||
|
||
This rule recommends having each `enum`s member value explicitly initialized. | ||
|
||
`enum`s are a practical way to organize semantically related constant values. However, by implicitly defining values, `enum`s can lead to unexpected bugs if it's modified without paying attention to the order of its items. | ||
|
||
## Rule Details | ||
|
||
`enum`s infers sequential numbers automatically when initializers are omitted: | ||
|
||
```ts | ||
enum Status { | ||
Open, // infer 0 | ||
Closed, // infer 1 | ||
} | ||
``` | ||
|
||
If a new member is added to the top of `Status`, both `Open` and `Closed` would have its values altered: | ||
|
||
```ts | ||
enum Status { | ||
Pending, // infer 0 | ||
Open, // infer 1 | ||
Closed, // infer 2 | ||
} | ||
``` | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```ts | ||
enum Status { | ||
Open = 1, | ||
Close, | ||
} | ||
|
||
enum Direction { | ||
Up, | ||
Down, | ||
} | ||
|
||
enum Color { | ||
Red, | ||
Green = 'Green' | ||
Blue = 'Blue', | ||
} | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```ts | ||
enum Status { | ||
Open = 'Open', | ||
Close = 'Close', | ||
} | ||
|
||
enum Direction { | ||
Up = 1, | ||
Down = 2, | ||
} | ||
|
||
enum Color { | ||
Red = 'Red', | ||
Green = 'Green', | ||
Blue = 'Blue', | ||
} | ||
``` | ||
|
||
## When Not To Use It | ||
|
||
If you don't care about `enum`s having implicit values you can safely disable this rule. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
packages/eslint-plugin/src/rules/prefer-enum-initializers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { TSESTree } from '@typescript-eslint/experimental-utils'; | ||
import * as util from '../util'; | ||
import { TSESLint } from '@typescript-eslint/experimental-utils'; | ||
|
||
type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion'; | ||
|
||
export default util.createRule<[], MessageIds>({ | ||
name: 'prefer-enum-initializers', | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Prefer initializing each enums member value', | ||
category: 'Best Practices', | ||
recommended: false, | ||
suggestion: true, | ||
}, | ||
messages: { | ||
defineInitializer: | ||
"The value of the member '{{ name }}' should be explicitly defined", | ||
defineInitializerSuggestion: | ||
'Can be fixed to {{ name }} = {{ suggested }}', | ||
}, | ||
schema: [], | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
const sourceCode = context.getSourceCode(); | ||
|
||
function TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { | ||
const { members } = node; | ||
|
||
members.forEach((member, index) => { | ||
if (member.initializer == null) { | ||
const name = sourceCode.getText(member); | ||
context.report({ | ||
node: member, | ||
messageId: 'defineInitializer', | ||
data: { | ||
name, | ||
}, | ||
suggest: [ | ||
{ | ||
messageId: 'defineInitializerSuggestion', | ||
data: { name, suggested: index }, | ||
fix: (fixer): TSESLint.RuleFix => { | ||
return fixer.replaceText(member, `${name} = ${index}`); | ||
}, | ||
}, | ||
{ | ||
messageId: 'defineInitializerSuggestion', | ||
data: { name, suggested: index + 1 }, | ||
fix: (fixer): TSESLint.RuleFix => { | ||
return fixer.replaceText(member, `${name} = ${index + 1}`); | ||
}, | ||
}, | ||
{ | ||
messageId: 'defineInitializerSuggestion', | ||
data: { name, suggested: `'${name}'` }, | ||
fix: (fixer): TSESLint.RuleFix => { | ||
return fixer.replaceText(member, `${name} = '${name}'`); | ||
}, | ||
}, | ||
], | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
return { | ||
TSEnumDeclaration, | ||
}; | ||
}, | ||
}); |
Oops, something went wrong.