Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [no-magic-numbers] add ignoreReadonlyClassProperties option #938

28 changes: 28 additions & 0 deletions packages/eslint-plugin/docs/rules/no-magic-numbers.md
Expand Up @@ -41,6 +41,34 @@ Examples of **correct** code for the `{ "ignoreNumericLiteralTypes": true }` opt
type SmallPrimes = 2 | 3 | 5 | 7 | 11;
```

### ignoreReadonlyClassProperties

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

```ts
/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreReadonlyClassProperties": false }]*/

class Foo {
readonly A = 1;
readonly B = 2;
public static readonly C = 1;
static readonly D = 1;
}
```

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

```ts
/*eslint @typescript-eslint/no-magic-numbers: ["error", { "ignoreReadonlyClassProperties": true }]*/

class Foo {
readonly A = 1;
readonly B = 2;
public static readonly C = 1;
static readonly D = 1;
}
```

### ignoreEnums

A boolean to specify if enums used in Typescript are considered okay. `false` by default.
Expand Down
25 changes: 25 additions & 0 deletions packages/eslint-plugin/src/rules/no-magic-numbers.ts
Expand Up @@ -33,6 +33,9 @@ export default util.createRule<Options, MessageIds>({
ignoreEnums: {
type: 'boolean',
},
ignoreReadonlyClassProperties: {
type: 'boolean',
},
},
},
],
Expand All @@ -46,6 +49,7 @@ export default util.createRule<Options, MessageIds>({
detectObjects: false,
ignoreNumericLiteralTypes: false,
ignoreEnums: false,
ignoreReadonlyClassProperties: false,
},
],
create(context, [options]) {
Expand Down Expand Up @@ -149,13 +153,34 @@ export default util.createRule<Options, MessageIds>({
return false;
}

/**
* Checks if the node parent is a readonly class property
* @param node the node to be validated.
* @returns true if the node parent is a readonly class property
* @private
*/
function isParentTSReadonlyClassProperty(node: TSESTree.Node): boolean {
return (
!!node.parent &&
node.parent.type === AST_NODE_TYPES.ClassProperty &&
!!node.parent.readonly
);
}

return {
Literal(node): void {
// Check if the node is a TypeScript enum declaration
if (options.ignoreEnums && isParentTSEnumDeclaration(node)) {
return;
}

if (
options.ignoreReadonlyClassProperties &&
isParentTSReadonlyClassProperty(node)
) {
return;
}

// Check TypeScript specific nodes for Numeric Literal
if (
options.ignoreNumericLiteralTypes &&
Expand Down
44 changes: 44 additions & 0 deletions packages/eslint-plugin/tests/rules/no-magic-numbers.test.ts
Expand Up @@ -41,6 +41,17 @@ ruleTester.run('no-magic-numbers', rule, {
code: 'enum foo { SECOND = 1000, NUM = "0123456789" }',
options: [{ ignoreEnums: true }],
},
{
code: `
class Foo {
readonly A = 1;
readonly B = 2;
public static readonly C = 1;
static readonly D = 1;
}
`,
options: [{ ignoreReadonlyClassProperties: true }],
},
],

invalid: [
Expand Down Expand Up @@ -166,5 +177,38 @@ ruleTester.run('no-magic-numbers', rule, {
},
],
},
{
code: `
class Foo {
readonly A = 1;
readonly B = 2;
public static readonly C = 1;
static readonly D = 1;
}
`,
options: [{ ignoreReadonlyClassProperties: false }],
errors: [
{
messageId: 'noMagic',
line: 3,
column: 16,
},
{
messageId: 'noMagic',
line: 4,
column: 16,
},
{
messageId: 'noMagic',
line: 5,
column: 30,
},
{
messageId: 'noMagic',
line: 6,
column: 23,
},
],
},
],
});
1 change: 1 addition & 0 deletions packages/eslint-plugin/typings/eslint-rules.d.ts
Expand Up @@ -199,6 +199,7 @@ declare module 'eslint/lib/rules/no-magic-numbers' {
detectObjects?: boolean;
ignoreNumericLiteralTypes?: boolean;
ignoreEnums?: boolean;
ignoreReadonlyClassProperties?: boolean;
},
],
{
Expand Down