Skip to content

Commit

Permalink
feat(eslint-plugin): [prefer-literal-enum-member] add allowBitwiseExp…
Browse files Browse the repository at this point in the history
…ressions option (#3515)
  • Loading branch information
a-tarasyuk committed Jun 13, 2021
1 parent 9e343fd commit 288092a
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 5 deletions.
35 changes: 35 additions & 0 deletions packages/eslint-plugin/docs/rules/prefer-literal-enum-member.md
Expand Up @@ -21,6 +21,10 @@ The answer is that `Foo.c` will be `1` at runtime. The [playground](https://www.

This rule is meant to prevent unexpected results in code by requiring the use of literal values as enum members to prevent unexpected runtime behavior. Template literals, arrays, objects, constructors, and all other expression types can end up using a variable from its scope or the parent scope, which can result in the same unexpected behavior at runtime.

## Options

- `allowBitwiseExpressions` set to `true` will allow you to use bitwise expressions in enum initializer (Default: `false`).

Examples of **incorrect** code for this rule:

```ts
Expand All @@ -46,6 +50,37 @@ enum Valid {
}
```

### `allowBitwiseExpressions`

Examples of **incorrect** code for the `{ "allowBitwiseExpressions": true }` option:

```ts
const x = 1;
enum Foo {
A = x << 0,
B = x >> 0,
C = x >>> 0,
D = x | 0,
E = x & 0,
F = x ^ 0,
G = ~x,
}
```

Examples of **correct** code for the `{ "allowBitwiseExpressions": true }` option:

```ts
enum Foo {
A = 1 << 0,
B = 1 >> 0,
C = 1 >>> 0,
D = 1 | 0,
E = 1 & 0,
F = 1 ^ 0,
G = ~1,
}
```

## When Not To Use It

If you want use anything other than simple literals as an enum value.
37 changes: 32 additions & 5 deletions packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts
Expand Up @@ -15,10 +15,24 @@ export default createRule({
messages: {
notLiteral: `Explicit enum value must only be a literal value (string, number, boolean, etc).`,
},
schema: [],
schema: [
{
type: 'object',
properties: {
allowBitwiseExpressions: {
type: 'boolean',
},
},
additionalProperties: false,
},
],
},
defaultOptions: [],
create(context) {
defaultOptions: [
{
allowBitwiseExpressions: false,
},
],
create(context, [{ allowBitwiseExpressions }]) {
return {
TSEnumMember(node): void {
// If there is no initializer, then this node is just the name of the member, so ignore.
Expand All @@ -39,8 +53,21 @@ export default createRule({
// -1 and +1
if (
node.initializer.type === AST_NODE_TYPES.UnaryExpression &&
['+', '-'].includes(node.initializer.operator) &&
node.initializer.argument.type === AST_NODE_TYPES.Literal
node.initializer.argument.type === AST_NODE_TYPES.Literal &&
(['+', '-'].includes(node.initializer.operator) ||
(allowBitwiseExpressions && node.initializer.operator === '~'))
) {
return;
}

if (
allowBitwiseExpressions &&
node.initializer.type === AST_NODE_TYPES.BinaryExpression &&
['|', '&', '^', '<<', '>>', '>>>'].includes(
node.initializer.operator,
) &&
node.initializer.left.type === AST_NODE_TYPES.Literal &&
node.initializer.right.type === AST_NODE_TYPES.Literal
) {
return;
}
Expand Down
117 changes: 117 additions & 0 deletions packages/eslint-plugin/tests/rules/prefer-literal-enum-member.test.ts
Expand Up @@ -62,6 +62,20 @@ enum ValidKeyWithComputedSyntaxButNoComputedKey {
['a'],
}
`,
{
code: `
enum Foo {
A = 1 << 0,
B = 1 >> 0,
C = 1 >>> 0,
D = 1 | 0,
E = 1 & 0,
F = 1 ^ 0,
G = ~1,
}
`,
options: [{ allowBitwiseExpressions: true }],
},
],
invalid: [
{
Expand Down Expand Up @@ -255,5 +269,108 @@ enum InvalidSpread {
},
],
},
{
code: `
enum Foo {
A = 1 << 0,
B = 1 >> 0,
C = 1 >>> 0,
D = 1 | 0,
E = 1 & 0,
F = 1 ^ 0,
G = ~1,
}
`,
options: [{ allowBitwiseExpressions: false }],
errors: [
{
messageId: 'notLiteral',
line: 3,
column: 3,
},
{
messageId: 'notLiteral',
line: 4,
column: 3,
},
{
messageId: 'notLiteral',
line: 5,
column: 3,
},
{
messageId: 'notLiteral',
line: 6,
column: 3,
},
{
messageId: 'notLiteral',
line: 7,
column: 3,
},
{
messageId: 'notLiteral',
line: 8,
column: 3,
},
{
messageId: 'notLiteral',
line: 9,
column: 3,
},
],
},
{
code: `
const x = 1;
enum Foo {
A = x << 0,
B = x >> 0,
C = x >>> 0,
D = x | 0,
E = x & 0,
F = x ^ 0,
G = ~x,
}
`,
options: [{ allowBitwiseExpressions: true }],
errors: [
{
messageId: 'notLiteral',
line: 4,
column: 3,
},
{
messageId: 'notLiteral',
line: 5,
column: 3,
},
{
messageId: 'notLiteral',
line: 6,
column: 3,
},
{
messageId: 'notLiteral',
line: 7,
column: 3,
},
{
messageId: 'notLiteral',
line: 8,
column: 3,
},
{
messageId: 'notLiteral',
line: 9,
column: 3,
},
{
messageId: 'notLiteral',
line: 10,
column: 3,
},
],
},
],
});

0 comments on commit 288092a

Please sign in to comment.