From 9d72475f35bc069a3bf8a3180f99c95e3399df20 Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Tue, 4 Feb 2025 21:32:37 +0200 Subject: [PATCH 1/4] handle accessor property with class-methods-use-this --- .../src/rules/class-methods-use-this.ts | 32 +++++++++-- packages/eslint-plugin/src/util/misc.ts | 1 + .../class-methods-use-this.test.ts | 55 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/class-methods-use-this.ts b/packages/eslint-plugin/src/rules/class-methods-use-this.ts index 31f4539a1793..773a658ef133 100644 --- a/packages/eslint-plugin/src/rules/class-methods-use-this.ts +++ b/packages/eslint-plugin/src/rules/class-methods-use-this.ts @@ -104,14 +104,20 @@ export default createRule({ } | { class: TSESTree.ClassDeclaration | TSESTree.ClassExpression; - member: TSESTree.MethodDefinition | TSESTree.PropertyDefinition; + member: + | TSESTree.AccessorProperty + | TSESTree.MethodDefinition + | TSESTree.PropertyDefinition; parent: Stack | undefined; usesThis: boolean; }; let stack: Stack | undefined; function pushContext( - member?: TSESTree.MethodDefinition | TSESTree.PropertyDefinition, + member?: + | TSESTree.AccessorProperty + | TSESTree.MethodDefinition + | TSESTree.PropertyDefinition, ): void { if (member?.parent.type === AST_NODE_TYPES.ClassBody) { stack = { @@ -135,7 +141,8 @@ export default createRule({ ): void { if ( node.parent.type === AST_NODE_TYPES.MethodDefinition || - node.parent.type === AST_NODE_TYPES.PropertyDefinition + node.parent.type === AST_NODE_TYPES.PropertyDefinition || + node.parent.type === AST_NODE_TYPES.AccessorProperty ) { pushContext(node.parent); } else { @@ -172,7 +179,8 @@ export default createRule({ node.static || (node.type === AST_NODE_TYPES.MethodDefinition && node.kind === 'constructor') || - (node.type === AST_NODE_TYPES.PropertyDefinition && + ((node.type === AST_NODE_TYPES.PropertyDefinition || + node.type === AST_NODE_TYPES.AccessorProperty) && !enforceForClassFields) ) { return false; @@ -242,6 +250,16 @@ export default createRule({ }, ...(enforceForClassFields ? { + 'AccessorProperty > ArrowFunctionExpression.value'( + node: TSESTree.ArrowFunctionExpression, + ): void { + enterFunction(node); + }, + 'AccessorProperty > ArrowFunctionExpression.value:exit'( + node: TSESTree.ArrowFunctionExpression, + ): void { + exitFunction(node); + }, 'PropertyDefinition > ArrowFunctionExpression.value'( node: TSESTree.ArrowFunctionExpression, ): void { @@ -258,6 +276,12 @@ export default createRule({ /* * Class field value are implicit functions. */ + 'AccessorProperty:exit'(): void { + popContext(); + }, + 'AccessorProperty > *.key:exit'(): void { + pushContext(); + }, 'PropertyDefinition:exit'(): void { popContext(); }, diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index 6313740bf0d0..b24758fd426a 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -239,6 +239,7 @@ export function isParenlessArrowFunction( } export type NodeWithKey = + | TSESTree.AccessorProperty | TSESTree.MemberExpression | TSESTree.MethodDefinition | TSESTree.Property diff --git a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts index 76155119c5cc..795d81db92b8 100644 --- a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts +++ b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts @@ -47,6 +47,45 @@ class Foo { }, { code: ` +class Foo { + accessor method = () => {}; +} + `, + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], + }, + { + code: ` +class Foo { + private accessor method = () => {}; +} + `, + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], + }, + { + code: ` +class Foo { + protected accessor method = () => {}; +} + `, + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], + }, + { + code: ` class Foo { #method() {} } @@ -577,6 +616,14 @@ class Foo implements Bar { }, { code: ` +class Foo implements Bar { + accessor method = () => {}; +} + `, + options: [{ ignoreClassesThatImplementAnInterface: true }], + }, + { + code: ` class Foo implements Bar { get getter() {} } @@ -617,6 +664,14 @@ class Foo { }, { code: ` +class Foo { + override accessor method = () => {}; +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` class Foo { override get getter(): number {} } From dec4b3a81e3b7cc599faa64880c7d2df7f45dc74 Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Thu, 6 Feb 2025 17:30:39 +0200 Subject: [PATCH 2/4] add missing tests --- .../docs/rules/class-methods-use-this.mdx | 2 +- .../class-methods-use-this.test.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx b/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx index 1cebdd15a0d5..ec4820a0e2fb 100644 --- a/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx +++ b/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; > See **https://typescript-eslint.io/rules/class-methods-use-this** for documentation. This rule extends the base [`eslint/class-methods-use-this`](https://eslint.org/docs/rules/class-methods-use-this) rule. -It adds support for ignoring `override` methods and/or methods on classes that implement an interface. +It adds support for ignoring `override` methods, methods on classes that implement an interface, and/or auto-accessors properties. ## Options diff --git a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts index 795d81db92b8..495b14269ccc 100644 --- a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts +++ b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts @@ -956,5 +956,23 @@ class Foo implements Bar { }, ], }, + { + code: ` +class Foo { + accessor method = () => { + this; + }; +} + `, + }, + { + code: ` +class Foo { + accessor method = function () { + this; + }; +} + `, + }, ], }); From eea44b3f5524bc81579945f2000b039fbfc665db Mon Sep 17 00:00:00 2001 From: Ronen Amiel Date: Thu, 6 Feb 2025 17:33:36 +0200 Subject: [PATCH 3/4] make wording clearer --- packages/eslint-plugin/docs/rules/class-methods-use-this.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx b/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx index ec4820a0e2fb..3f8f12210eda 100644 --- a/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx +++ b/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; > See **https://typescript-eslint.io/rules/class-methods-use-this** for documentation. This rule extends the base [`eslint/class-methods-use-this`](https://eslint.org/docs/rules/class-methods-use-this) rule. -It adds support for ignoring `override` methods, methods on classes that implement an interface, and/or auto-accessors properties. +It adds support for ignoring `override` methods and/or methods on classes that implement an interface. It also supports TypeScript's auto-accessor properties. ## Options From 6d1bc03e0935cbf028c57eacdbc68a39ca40bb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 17 Feb 2025 08:28:04 -0500 Subject: [PATCH 4/4] Update packages/eslint-plugin/docs/rules/class-methods-use-this.mdx --- packages/eslint-plugin/docs/rules/class-methods-use-this.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx b/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx index 3f8f12210eda..ad7bf8d8a2dd 100644 --- a/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx +++ b/packages/eslint-plugin/docs/rules/class-methods-use-this.mdx @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; > See **https://typescript-eslint.io/rules/class-methods-use-this** for documentation. This rule extends the base [`eslint/class-methods-use-this`](https://eslint.org/docs/rules/class-methods-use-this) rule. -It adds support for ignoring `override` methods and/or methods on classes that implement an interface. It also supports TypeScript's auto-accessor properties. +It adds support for ignoring `override` methods and/or methods on classes that implement an interface. It also supports auto-accessor properties. ## Options