From 9c8bbff16cad2c3bcd05f72d06168265c66384e7 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Tue, 30 Jan 2024 20:21:53 +0100 Subject: [PATCH 1/3] feat!: default for `enforceForClassMembers` in `no-useless-computed-key` --- docs/src/rules/no-useless-computed-key.md | 109 ++++++++++++--------- docs/src/use/migrate-to-9.0.0.md | 16 +++ lib/rules/no-useless-computed-key.js | 4 +- tests/lib/rules/no-useless-computed-key.js | 44 ++------- 4 files changed, 84 insertions(+), 89 deletions(-) diff --git a/docs/src/rules/no-useless-computed-key.md b/docs/src/rules/no-useless-computed-key.md index f0acb66cc0d..1a77b39bf05 100644 --- a/docs/src/rules/no-useless-computed-key.md +++ b/docs/src/rules/no-useless-computed-key.md @@ -33,6 +33,19 @@ var a = { ['0+1,234']: 0 }; var a = { [0]: 0 }; var a = { ['x']: 0 }; var a = { ['x']() {} }; + +class Foo { + ["foo"] = "bar"; + + [0]() {} + ['a']() {} + get ['b']() {} + set ['c'](value) {} + + static ["foo"] = "bar"; + + static ['a']() {} +} ``` ::: @@ -49,6 +62,19 @@ var c = { 0: 0 }; var a = { x() {} }; var c = { a: 0 }; var c = { '0+1,234': 0 }; + +class Foo { + "foo" = "bar"; + + 0() {} + 'a'() {} + get 'b'() {} + set 'c'(value) {} + + static "foo" = "bar"; + + static 'a'() {} +} ``` ::: @@ -65,6 +91,18 @@ var c = { ["__proto__"]: bar // defines a property named "__proto__" }; + +class Foo { + ["constructor"]; // instance field named "constructor" + + "constructor"() {} // the constructor of this class + + ["constructor"]() {} // method named "constructor" + + static ["constructor"]; // static field named "constructor" + + static ["prototype"]; // runtime error, it would be a parsing error without `[]` +} ``` ::: @@ -73,78 +111,51 @@ var c = { This rule has an object option: -* `enforceForClassMembers` set to `true` additionally applies this rule to class members (Default `false`). +* `enforceForClassMembers` set to `false` disables this rule for class members (Default `true`). ### enforceForClassMembers -By default, this rule does not check class declarations and class expressions, -as the default value for `enforceForClassMembers` is `false`. +By default, this rule also checks class declarations and class expressions, +as the default value for `enforceForClassMembers` is `true`. -When `enforceForClassMembers` is set to `true`, the rule will also disallow unnecessary computed keys inside of class fields, class methods, class getters, and class setters. +When `enforceForClassMembers` is set to `false`, the rule will allow unnecessary computed keys inside of class fields, class methods, class getters, and class setters. -Examples of **incorrect** code for this rule with the `{ "enforceForClassMembers": true }` option: +Examples of **incorrect** code for this rule with the `{ "enforceForClassMembers": false }` option: ::: incorrect ```js -/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/ +/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": false }]*/ -class Foo { - ["foo"] = "bar"; +const obj = { + ["foo"]: "bar", + [42]: "baz", - [0]() {} - ['a']() {} - get ['b']() {} + ['a']() {}, + get ['b']() {}, set ['c'](value) {} - - static ["foo"] = "bar"; - - static ['a']() {} -} -``` - -::: - -Examples of **correct** code for this rule with the `{ "enforceForClassMembers": true }` option: - -::: correct - -```js -/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/ - -class Foo { - "foo" = "bar"; - - 0() {} - 'a'() {} - get 'b'() {} - set 'c'(value) {} - - static "foo" = "bar"; - - static 'a'() {} -} +}; ``` ::: -Examples of additional **correct** code for this rule with the `{ "enforceForClassMembers": true }` option: +Examples of **correct** code for this rule with the `{ "enforceForClassMembers": false }` option: ::: correct ```js -/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/ - -class Foo { - ["constructor"]; // instance field named "constructor" +/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": false }]*/ - "constructor"() {} // the constructor of this class - - ["constructor"]() {} // method named "constructor" +class SomeClass { + ["foo"] = "bar"; + [42] = "baz"; - static ["constructor"]; // static field named "constructor" + ['a']() {} + get ['b']() {} + set ['c'](value) {} - static ["prototype"]; // runtime error, it would be a parsing error without `[]` + static ["foo"] = "bar"; + static ['baz']() {} } ``` diff --git a/docs/src/use/migrate-to-9.0.0.md b/docs/src/use/migrate-to-9.0.0.md index 7e0616c0d1a..4ff5691ba68 100644 --- a/docs/src/use/migrate-to-9.0.0.md +++ b/docs/src/use/migrate-to-9.0.0.md @@ -33,6 +33,7 @@ The lists below are ordered roughly by the number of users each change is expect * [`no-restricted-imports` now accepts multiple config entries with the same `name`](#no-restricted-imports) * [`"eslint:recommended"` and `"eslint:all"` strings no longer accepted in flat config](#string-config) * [`no-inner-declarations` has a new default behavior with a new option](#no-inner-declarations) +* [`no-useless-computed-key` flags unnecessary computed member names in classes by default](#no-useless-computed-key) ### Breaking changes for plugin developers @@ -362,6 +363,21 @@ if (test) { **Related issue(s):** [#15576](https://github.com/eslint/eslint/issues/15576) +## `no-useless-computed-key` flags unnecessary computed member names in classes by default + +In ESLint v9.0.0, the default value of the `enforceForClassMembers` option of the `no-useless-computed-key` rule was changed from `false` to `true`. +The effect of this change ist that unnecessary computed member names in classes will be flagged by default. + +```js +/*eslint no-useless-computed-key: "error"*/ + +class SomeClass { + ["someMethod"]() {} // ok in ESLint v8, error in ESLint v9. +} +``` + +**Related issue(s):** [#18042](https://github.com/eslint/eslint/issues/18042) + ## Removed multiple `context` methods ESLint v9.0.0 removes multiple deprecated methods from the `context` object and moves them onto the `SourceCode` object: diff --git a/lib/rules/no-useless-computed-key.js b/lib/rules/no-useless-computed-key.js index f2d9f3341f5..5cc652bea26 100644 --- a/lib/rules/no-useless-computed-key.js +++ b/lib/rules/no-useless-computed-key.js @@ -101,7 +101,7 @@ module.exports = { properties: { enforceForClassMembers: { type: "boolean", - default: false + default: true } }, additionalProperties: false @@ -114,7 +114,7 @@ module.exports = { }, create(context) { const sourceCode = context.sourceCode; - const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers; + const enforceForClassMembers = context.options[0]?.enforceForClassMembers ?? true; /** * Reports a given node if it violated this rule. diff --git a/tests/lib/rules/no-useless-computed-key.js b/tests/lib/rules/no-useless-computed-key.js index 121b320422d..52765a03453 100644 --- a/tests/lib/rules/no-useless-computed-key.js +++ b/tests/lib/rules/no-useless-computed-key.js @@ -33,10 +33,10 @@ ruleTester.run("no-useless-computed-key", rule, { { code: "(class { [x]() {} })", options: [{ enforceForClassMembers: true }] }, { code: "(class { ['constructor']() {} })", options: [{ enforceForClassMembers: true }] }, { code: "(class { static ['prototype']() {} })", options: [{ enforceForClassMembers: true }] }, - "class Foo { ['x']() {} }", - "(class { ['x']() {} })", - "class Foo { static ['constructor']() {} }", - "class Foo { ['prototype']() {} }", + "class Foo { 'x'() {} }", + "(class { [x]() {} })", + "class Foo { static constructor() {} }", + "class Foo { prototype() {} }", { code: "class Foo { ['x']() {} }", options: [{ enforceForClassMembers: false }] }, { code: "(class { ['x']() {} })", options: [{ enforceForClassMembers: false }] }, { code: "class Foo { static ['constructor']() {} }", options: [{ enforceForClassMembers: false }] }, @@ -272,7 +272,7 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['0+1,234']() {} }", output: "class Foo { '0+1,234'() {} }", - options: [{ enforceForClassMembers: true }], + options: [{ }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'0+1,234'" }, @@ -281,7 +281,7 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['x']() {} }", output: "class Foo { 'x'() {} }", - options: [{ enforceForClassMembers: true }], + options: [{ enforceForClassMembers: void 0 }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'x'" }, @@ -290,7 +290,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { [/* this comment prevents a fix */ 'x']() {} }", output: null, - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'x'" }, @@ -299,7 +298,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['x' /* this comment also prevents a fix */]() {} }", output: null, - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'x'" }, @@ -308,7 +306,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { [('x')]() {} }", output: "class Foo { 'x'() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'x'" }, @@ -317,7 +314,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { *['x']() {} }", output: "class Foo { *'x'() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'x'" }, @@ -326,7 +322,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { async ['x']() {} }", output: "class Foo { async 'x'() {} }", - options: [{ enforceForClassMembers: true }], languageOptions: { ecmaVersion: 8 }, errors: [{ messageId: "unnecessarilyComputedProperty", @@ -336,7 +331,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { get[.2]() {} }", output: "class Foo { get.2() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: ".2" }, @@ -345,7 +339,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { set[.2](value) {} }", output: "class Foo { set.2(value) {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: ".2" }, @@ -354,7 +347,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { async[.2]() {} }", output: "class Foo { async.2() {} }", - options: [{ enforceForClassMembers: true }], languageOptions: { ecmaVersion: 8 }, errors: [{ messageId: "unnecessarilyComputedProperty", @@ -364,7 +356,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { [2]() {} }", output: "class Foo { 2() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -373,7 +364,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { get [2]() {} }", output: "class Foo { get 2() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -382,7 +372,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { set [2](value) {} }", output: "class Foo { set 2(value) {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -391,7 +380,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { async [2]() {} }", output: "class Foo { async 2() {} }", - options: [{ enforceForClassMembers: true }], languageOptions: { ecmaVersion: 8 }, errors: [{ messageId: "unnecessarilyComputedProperty", @@ -401,7 +389,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { get[2]() {} }", output: "class Foo { get 2() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -410,7 +397,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { set[2](value) {} }", output: "class Foo { set 2(value) {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -419,7 +405,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { async[2]() {} }", output: "class Foo { async 2() {} }", - options: [{ enforceForClassMembers: true }], languageOptions: { ecmaVersion: 8 }, errors: [{ messageId: "unnecessarilyComputedProperty", @@ -429,7 +414,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { get['foo']() {} }", output: "class Foo { get'foo'() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'foo'" }, @@ -438,7 +422,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { *[2]() {} }", output: "class Foo { *2() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -447,7 +430,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { async*[2]() {} }", output: "class Foo { async*2() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "2" }, @@ -456,7 +438,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { static ['constructor']() {} }", output: "class Foo { static 'constructor'() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'constructor'" }, @@ -465,7 +446,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['prototype']() {} }", output: "class Foo { 'prototype'() {} }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'prototype'" }, @@ -474,7 +454,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { ['x']() {} })", output: "(class { 'x'() {} })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'x'" }, @@ -483,7 +462,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { ['__proto__']() {} })", output: "(class { '__proto__'() {} })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'__proto__'" }, @@ -492,7 +470,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { static ['__proto__']() {} })", output: "(class { static '__proto__'() {} })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'__proto__'" }, @@ -501,7 +478,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { static ['constructor']() {} })", output: "(class { static 'constructor'() {} })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'constructor'" }, @@ -510,7 +486,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { ['prototype']() {} })", output: "(class { 'prototype'() {} })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'prototype'" }, @@ -519,7 +494,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['0'] }", output: "class Foo { '0' }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'0'" }, @@ -528,7 +502,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['0'] = 0 }", output: "class Foo { '0' = 0 }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'0'" }, @@ -537,7 +510,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { static[0] }", output: "class Foo { static 0 }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "0" }, @@ -546,7 +518,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "class Foo { ['#foo'] }", output: "class Foo { '#foo' }", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'#foo'" }, @@ -555,7 +526,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { ['__proto__'] })", output: "(class { '__proto__' })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'__proto__'" }, @@ -564,7 +534,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { static ['__proto__'] })", output: "(class { static '__proto__' })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'__proto__'" }, @@ -573,7 +542,6 @@ ruleTester.run("no-useless-computed-key", rule, { }, { code: "(class { ['prototype'] })", output: "(class { 'prototype' })", - options: [{ enforceForClassMembers: true }], errors: [{ messageId: "unnecessarilyComputedProperty", data: { property: "'prototype'" }, From f74e37a39b27f59016c167559e69cf3d229fff90 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 31 Jan 2024 20:50:33 +0100 Subject: [PATCH 2/3] Fix a typo Co-authored-by: Milos Djermanovic --- docs/src/use/migrate-to-9.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/use/migrate-to-9.0.0.md b/docs/src/use/migrate-to-9.0.0.md index 4ff5691ba68..66b2ce63316 100644 --- a/docs/src/use/migrate-to-9.0.0.md +++ b/docs/src/use/migrate-to-9.0.0.md @@ -366,7 +366,7 @@ if (test) { ## `no-useless-computed-key` flags unnecessary computed member names in classes by default In ESLint v9.0.0, the default value of the `enforceForClassMembers` option of the `no-useless-computed-key` rule was changed from `false` to `true`. -The effect of this change ist that unnecessary computed member names in classes will be flagged by default. +The effect of this change is that unnecessary computed member names in classes will be flagged by default. ```js /*eslint no-useless-computed-key: "error"*/ From afbb3368f7ea75f1935261c7a2d95e99d0ff8e97 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 31 Jan 2024 20:56:36 +0100 Subject: [PATCH 3/3] Add "To address" section --- docs/src/use/migrate-to-9.0.0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/use/migrate-to-9.0.0.md b/docs/src/use/migrate-to-9.0.0.md index 66b2ce63316..1dc99d955dc 100644 --- a/docs/src/use/migrate-to-9.0.0.md +++ b/docs/src/use/migrate-to-9.0.0.md @@ -376,6 +376,8 @@ class SomeClass { } ``` +**To address:** Fix the problems reported by the rule or revert to the previous behavior by setting the `enforceForClassMembers` option to `false`. + **Related issue(s):** [#18042](https://github.com/eslint/eslint/issues/18042) ## Removed multiple `context` methods