Skip to content

Commit 677a283

Browse files
authored
feat: add auto-accessor fields support to class-methods-use-this (#19789)
* feat: add auto-accessor fields support to class-methods-use-this * add more tests * docs: clarify auto-accessor fields
1 parent 3aed075 commit 677a283

File tree

3 files changed

+131
-2
lines changed

3 files changed

+131
-2
lines changed

docs/src/rules/class-methods-use-this.md

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class C {
109109
This rule has four options:
110110

111111
* `"exceptMethods"` allows specified method names to be ignored with this rule.
112-
* `"enforceForClassFields"` enforces that functions used as instance field initializers utilize `this`. (default: `true`)
112+
* `"enforceForClassFields"` enforces that arrow functions and function expressions used as instance field initializers utilize `this`. This also applies to auto-accessor fields (fields declared with the `accessor` keyword) which are part of the [stage 3 decorators proposal](https://github.com/tc39/proposal-decorators). (default: `true`)
113113
* `"ignoreOverrideMethods"` ignores members that are marked with the `override` modifier. (TypeScript only, default: `false`)
114114
* `"ignoreClassesWithImplements"` ignores class members that are defined within a class that `implements` an interface. (TypeScript only)
115115

@@ -159,7 +159,7 @@ class A {
159159
"class-methods-use-this": [<enabled>, { "enforceForClassFields": true | false }]
160160
```
161161

162-
The `enforceForClassFields` option enforces that arrow functions and function expressions used as instance field initializers utilize `this`. (default: `true`)
162+
The `enforceForClassFields` option enforces that arrow functions and function expressions used as instance field initializers utilize `this`. This also applies to auto-accessor fields (fields declared with the `accessor` keyword) which are part of the [stage 3 decorators proposal](https://github.com/tc39/proposal-decorators). (default: `true`)
163163

164164
Examples of **incorrect** code for this rule with the `{ "enforceForClassFields": true }` option (default):
165165

@@ -203,6 +203,51 @@ class A {
203203

204204
:::
205205

206+
Examples of **incorrect** TypeScript code for this rule with the `{ "enforceForClassFields": true }` option (default):
207+
208+
::: incorrect
209+
210+
```ts
211+
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
212+
213+
class A {
214+
foo = () => {}
215+
accessor bar = () => {}
216+
}
217+
```
218+
219+
:::
220+
221+
Examples of **correct** TypeScript code for this rule with the `{ "enforceForClassFields": true }` option (default):
222+
223+
::: correct
224+
225+
```ts
226+
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
227+
228+
class A {
229+
foo = () => {this;}
230+
accessor bar = () => {this;}
231+
}
232+
```
233+
234+
:::
235+
236+
Examples of **correct** TypeScript code for this rule with the `{ "enforceForClassFields": false }` option:
237+
238+
::: correct
239+
240+
```ts
241+
/*eslint class-methods-use-this: ["error", { "enforceForClassFields": false }] */
242+
243+
class A {
244+
foo = () => {}
245+
accessor bar = () => {}
246+
}
247+
```
248+
249+
:::
250+
206251
### ignoreOverrideMethods
207252

208253
```ts

lib/rules/class-methods-use-this.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ module.exports = {
111111
switch (node.type) {
112112
case "MethodDefinition":
113113
return !node.static && node.kind !== "constructor";
114+
case "AccessorProperty":
114115
case "PropertyDefinition":
115116
return !node.static && enforceForClassFields;
116117
default:
@@ -218,6 +219,8 @@ module.exports = {
218219
/*
219220
* Class field value are implicit functions.
220221
*/
222+
"AccessorProperty > *.key:exit": pushContext,
223+
"AccessorProperty:exit": popContext,
221224
"PropertyDefinition > *.key:exit": pushContext,
222225
"PropertyDefinition:exit": popContext,
223226

@@ -233,6 +236,10 @@ module.exports = {
233236
ThisExpression: markThisUsed,
234237
Super: markThisUsed,
235238
...(enforceForClassFields && {
239+
"AccessorProperty > ArrowFunctionExpression.value":
240+
enterFunction,
241+
"AccessorProperty > ArrowFunctionExpression.value:exit":
242+
exitFunction,
236243
"PropertyDefinition > ArrowFunctionExpression.value":
237244
enterFunction,
238245
"PropertyDefinition > ArrowFunctionExpression.value:exit":

tests/lib/rules/class-methods-use-this.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,14 @@ ruleTesterTypeScript.run("class-methods-use-this", rule, {
483483
{ code: "class A { 42() { } }", options: [{ exceptMethods: ["42"] }] },
484484
"class A { foo = function() {this} }",
485485
"class A { foo = () => {this} }",
486+
"class A { accessor foo = function() {this} }",
487+
"class A { accessor foo = () => {this} }",
488+
"class A { accessor foo = 1; }",
486489
"class A { foo = () => {super.toString} }",
487490
"class A { static foo = function() {} }",
488491
"class A { static foo = () => {} }",
492+
"class A { static accessor foo = function() {} }",
493+
"class A { static accessor foo = () => {} }",
489494
{
490495
code: "class A { #bar() {} }",
491496
options: [{ exceptMethods: ["#bar"] }],
@@ -498,6 +503,14 @@ ruleTesterTypeScript.run("class-methods-use-this", rule, {
498503
code: "class A { foo = () => {} }",
499504
options: [{ enforceForClassFields: false }],
500505
},
506+
{
507+
code: "class A { accessor foo = function () {} }",
508+
options: [{ enforceForClassFields: false }],
509+
},
510+
{
511+
code: "class A { accessor foo = () => {} }",
512+
options: [{ enforceForClassFields: false }],
513+
},
501514
{
502515
code: "class A { override foo = () => {} }",
503516
options: [{ enforceForClassFields: false }],
@@ -728,6 +741,70 @@ ruleTesterTypeScript.run("class-methods-use-this", rule, {
728741
},
729742
{
730743
code: `
744+
class Foo {
745+
accessor method = function () {}
746+
}
747+
`,
748+
errors: [
749+
{
750+
messageId: "missingThis",
751+
},
752+
],
753+
},
754+
{
755+
code: `
756+
class Foo {
757+
accessor method = () => {}
758+
}
759+
`,
760+
errors: [
761+
{
762+
messageId: "missingThis",
763+
},
764+
],
765+
},
766+
{
767+
code: `
768+
class Foo {
769+
private accessor method = () => {}
770+
}
771+
`,
772+
errors: [
773+
{
774+
messageId: "missingThis",
775+
},
776+
],
777+
},
778+
{
779+
code: `
780+
class Foo {
781+
protected accessor method = () => {}
782+
}
783+
`,
784+
errors: [
785+
{
786+
messageId: "missingThis",
787+
},
788+
],
789+
},
790+
{
791+
code: `
792+
class A {
793+
foo() {
794+
return class {
795+
accessor bar = this;
796+
};
797+
}
798+
}
799+
`,
800+
errors: [
801+
{
802+
messageId: "missingThis",
803+
},
804+
],
805+
},
806+
{
807+
code: `
731808
class Derived extends Base {
732809
override method() {}
733810
}

0 commit comments

Comments
 (0)