Repository: eslint/eslint
Pull request #19678: feat: add TypeScript support to `prefer-arrow-callback`
Author: login: Tanujkanti4441
Labels: name: rule
description: Relates to ESLint's core rules
name: accepted
description: There is consensus among the team that this change meets the criteria for inclusion
name: feature
description: This change adds a new feature to ESLint
Created: 2025-05-02T11:00:54Z | Merged: 2025-05-28T11:28:11Z

<!--
    Thank you for contributing!

    ESLint adheres to the [OpenJS Foundation Code of Conduct](https://eslint.org/conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
[x] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
[ ] Add autofix to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/latest/contribute/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)
Added TypeScript support to `prefer-arrow-callback`. Now rule won't autofix following case:

```ts
declare function test(str: string, callback: any): void

test('clean', function (this: any) {
  //...
});
```

#### Is there anything you'd like reviewers to focus on?
Based on https://github.com/eslint/eslint/issues/19173#issuecomment-2846524902

<!-- markdownlint-disable-file MD004 -->



Diff:
diff --git a/docs/src/rules/prefer-arrow-callback.md b/docs/src/rules/prefer-arrow-callback.md
index 3ef1fb43f1e4..6e5f72a0ec1a 100644
--- a/docs/src/rules/prefer-arrow-callback.md
+++ b/docs/src/rules/prefer-arrow-callback.md
@@ -64,6 +64,36 @@ foo(function bar(n) { return n && n + bar(n - 1); }); // OK
 
 :::
 
+This rule additionally supports TypeScript type syntax.
+
+Examples of **incorrect** TypeScript code for this rule:
+
+::: incorrect
+
+```ts
+/*eslint prefer-arrow-callback: "error"*/
+
+foo(function bar(a: string) { a; });
+
+test('foo', function (this: any) {});
+```
+
+:::
+
+Examples of **correct** TypeScript code for this rule:
+
+::: correct
+
+```ts
+/*eslint prefer-arrow-callback: "error"*/
+
+foo((a: string) => a);
+
+const foo = function foo(bar: any) {};
+```
+
+:::
+
 ## Options
 
 Access further control over this rule's behavior via an options object.
@@ -88,6 +118,30 @@ foo(function bar() {});
 
 :::
 
+Examples of **incorrect** TypeScript code for this rule with `{ "allowNamedFunctions": true }`:
+
+::: incorrect
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
+
+foo(function(a: string) {});
+```
+
+:::
+
+Examples of **correct** TypeScript code for this rule with `{ "allowNamedFunctions": true }`:
+
+::: correct
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
+
+foo(function bar(a: string) {});
+```
+
+:::
+
 ### allowUnboundThis
 
 By default `{ "allowUnboundThis": true }`, this `boolean` option allows function expressions containing `this` to be used as callbacks, as long as the function in question has not been explicitly bound.
@@ -110,6 +164,20 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+Examples of **incorrect** TypeScript code for this rule with `{ "allowUnboundThis": false }`:
+
+::: incorrect
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowUnboundThis": false } ] */
+
+foo(function(a: string) { this; });
+
+foo(function(a: string) { (() => this); });
+```
+
+:::
+
 ## When Not To Use It
 
 * In environments that have not yet adopted ES6 language features (ES3/5).
diff --git a/lib/rules/prefer-arrow-callback.js b/lib/rules/prefer-arrow-callback.js
index cbbf5f9bf6d2..0f05b52ad06a 100644
--- a/lib/rules/prefer-arrow-callback.js
+++ b/lib/rules/prefer-arrow-callback.js
@@ -151,6 +151,8 @@ function hasDuplicateParams(paramsList) {
 module.exports = {
 	meta: {
 		type: "suggestion",
+		dialects: ["javascript", "typescript"],
+		language: "javascript",
 
 		defaultOptions: [
 			{ allowNamedFunctions: false, allowUnboundThis: true },
@@ -308,6 +310,13 @@ module.exports = {
 								return;
 							}
 
+							if (
+								node.params.length &&
+								node.params[0].name === "this"
+							) {
+								return;
+							}
+
 							// Remove `.bind(this)` if exists.
 							if (callbackInfo.isLexicalThis) {
 								const memberNode = node.parent;
diff --git a/tests/lib/rules/prefer-arrow-callback.js b/tests/lib/rules/prefer-arrow-callback.js
index d90c3e93ced7..e3588ece3043 100644
--- a/tests/lib/rules/prefer-arrow-callback.js
+++ b/tests/lib/rules/prefer-arrow-callback.js
@@ -255,3 +255,251 @@ ruleTester.run("prefer-arrow-callback", rule, {
 		},
 	],
 });
+
+const ruleTesterTypeScript = new RuleTester({
+	languageOptions: {
+		parser: require("@typescript-eslint/parser"),
+	},
+});
+
+ruleTesterTypeScript.run("prefer-arrow-callback", rule, {
+	valid: [
+		"foo(a => a);",
+		"foo((a:string) => a);",
+		"foo(function*() {});",
+		"foo(function() { this; });",
+		{
+			code: "foo(function bar(a:string) {});",
+			options: [{ allowNamedFunctions: true }],
+		},
+		"foo(function() { (() => this); });",
+		"foo(function() { this; }.bind(obj));",
+		"foo(function() { this; }.call(this));",
+		"foo(a => { (function() {}); });",
+		"var foo = function foo() {};",
+		"(function foo() {})();",
+		"foo(function bar() { bar; });",
+		"foo(function bar() { arguments; });",
+		"foo(function bar() { arguments; }.bind(this));",
+		"foo(function bar() { new.target; });",
+		"foo(function bar() { new.target; }.bind(this));",
+		"foo(function bar() { this; }.bind(this, somethingElse));",
+		"foo((function() {}).bind.bar)",
+		"foo((function() { this.bar(); }).bind(obj).bind(this))",
+		"test('clean', function (this: any) { this.foo = 'Cleaned!';});",
+		"obj.test('clean', function (foo) { this.foo = 'Cleaned!'; });",
+	],
+	invalid: [
+		{
+			code: "foo(function bar() {});",
+			output: "foo(() => {});",
+			errors,
+		},
+		{
+			code: "foo(function(a:string) {});",
+			output: "foo((a:string) => {});",
+			options: [{ allowNamedFunctions: true }],
+			errors,
+		},
+		{
+			code: "foo(function bar() {});",
+			output: "foo(() => {});",
+			options: [{ allowNamedFunctions: false }],
+			errors,
+		},
+		{
+			code: "foo(function() {});",
+			output: "foo(() => {});",
+			errors,
+		},
+		{
+			code: "foo(nativeCb || function() {});",
+			output: "foo(nativeCb || (() => {}));",
+			errors,
+		},
+		{
+			code: "foo(bar ? function() {} : function() {});",
+			output: "foo(bar ? () => {} : () => {});",
+			errors: [errors[0], errors[0]],
+		},
+		{
+			code: "foo(function() { (function() { this; }); });",
+			output: "foo(() => { (function() { this; }); });",
+			errors,
+		},
+		{
+			code: "foo(function() { this; }.bind(this));",
+			output: "foo(() => { this; });",
+			errors,
+		},
+		{
+			code: "foo(bar || function() { this; }.bind(this));",
+			output: "foo(bar || (() => { this; }));",
+			errors,
+		},
+		{
+			code: "foo(function() { (() => this); }.bind(this));",
+			output: "foo(() => { (() => this); });",
+			errors,
+		},
+		{
+			code: "foo(function bar(a:string) { a; });",
+			output: "foo((a:string) => { a; });",
+			errors,
+		},
+		{
+			code: "foo(function(a:any) { a; });",
+			output: "foo((a:any) => { a; });",
+			errors,
+		},
+		{
+			code: "foo(function(arguments:any) { arguments; });",
+			output: "foo((arguments:any) => { arguments; });",
+			errors,
+		},
+		{
+			code: "foo(function(a:string) { this; });",
+			output: null, // No fix applied
+			options: [{ allowUnboundThis: false }],
+			errors,
+		},
+		{
+			code: "foo(function() { (() => this); });",
+			output: null, // No fix applied
+			options: [{ allowUnboundThis: false }],
+			errors,
+		},
+		{
+			code: "qux(function(foo:string, bar:number, baz:string) { return foo * 2; })",
+			output: "qux((foo:string, bar:number, baz:string) => { return foo * 2; })",
+			errors,
+		},
+		{
+			code: "qux(function(foo:number, bar:number, baz:number) { return foo * bar; }.bind(this))",
+			output: "qux((foo:number, bar:number, baz:number) => { return foo * bar; })",
+			errors,
+		},
+		{
+			code: "qux(function(foo:any, bar:any, baz:any) { return foo * this.qux; }.bind(this))",
+			output: "qux((foo:any, bar:any, baz:any) => { return foo * this.qux; })",
+			errors,
+		},
+		{
+			code: "foo(function() {}.bind(this, somethingElse))",
+			output: "foo((() => {}).bind(this, somethingElse))",
+			errors,
+		},
+		{
+			code: "qux(function(foo = 1, [bar = 2] = [], {qux: baz = 3} = {foo: 'bar'}) { return foo + bar; });",
+			output: "qux((foo = 1, [bar = 2] = [], {qux: baz = 3} = {foo: 'bar'}) => { return foo + bar; });",
+			errors,
+		},
+		{
+			code: "qux(function(baz:string, baz:string) { })",
+			output: null, // Duplicate parameter names are a SyntaxError in arrow functions
+			errors,
+		},
+		{
+			code: "qux(function( /* no params */ ) { })",
+			output: "qux(( /* no params */ ) => { })",
+			errors,
+		},
+		{
+			code: "qux(function( /* a */ foo:string /* b */ , /* c */ bar:string /* d */ , /* e */ baz:string /* f */ ) { return foo; })",
+			output: "qux(( /* a */ foo:string /* b */ , /* c */ bar:string /* d */ , /* e */ baz:string /* f */ ) => { return foo; })",
+			errors,
+		},
+		{
+			code: "qux(async function (foo:number = 1, bar:number = 2, baz:number = 3) { return baz; })",
+			output: "qux(async (foo:number = 1, bar:number = 2, baz:number = 3) => { return baz; })",
+			errors,
+		},
+		{
+			code: "qux(async function (foo:number = 1, bar:number = 2, baz:number = 3) { return this; }.bind(this))",
+			output: "qux(async (foo:number = 1, bar:number = 2, baz:number = 3) => { return this; })",
+			errors,
+		},
+		{
+			code: "foo((bar || function() {}).bind(this))",
+			output: null,
+			errors,
+		},
+		{
+			code: "foo(function() {}.bind(this).bind(obj))",
+			output: "foo((() => {}).bind(obj))",
+			errors,
+		},
+
+		// Optional chaining
+		{
+			code: "foo?.(function() {});",
+			output: "foo?.(() => {});",
+			errors,
+		},
+		{
+			code: "foo?.(function() { return this; }.bind(this));",
+			output: "foo?.(() => { return this; });",
+			errors,
+		},
+		{
+			code: "foo(function() { return this; }?.bind(this));",
+			output: "foo(() => { return this; });",
+			errors,
+		},
+		{
+			code: "foo((function() { return this; }?.bind)(this));",
+			output: null,
+			errors,
+		},
+
+		// https://github.com/eslint/eslint/issues/16718
+		{
+			code: `
+            test(
+                function ()
+                { }
+            );
+            `,
+			output: `
+            test(
+                () =>
+                { }
+            );
+            `,
+			errors,
+		},
+		{
+			code: `
+            test(
+                function (
+                    ...args
+                ) /* Lorem ipsum
+                dolor sit amet. */ {
+                    return args;
+                }
+            );
+            `,
+			output: `
+            test(
+                (
+                    ...args
+                ) => /* Lorem ipsum
+                dolor sit amet. */ {
+                    return args;
+                }
+            );
+            `,
+			errors,
+		},
+		{
+			code: "foo(function():string { return 'foo' });",
+			output: "foo(():string => { return 'foo' });",
+			errors,
+		},
+		{
+			code: "test('foo', function (this: any) {});",
+			output: null,
+			errors,
+		},
+	],
+});


Reviews:
user: login: JoshuaKGoldberg
body: Seems pretty reasonable - just a couple of requests, wdyt?
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T13:38:18Z
user: login: Tanujkanti4441
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T15:49:22Z
user: login: snitin315
body: Overall looks good, few comments. Also, can you fix the CI check?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:32:01Z
user: login: JoshuaKGoldberg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:03:35Z
user: login: JoshuaKGoldberg
body: I defer to @snitin315 on the docs points 😄 everything looks great, thanks!

The formatting stuff I was thinking normally gets auto-fixed on commit. 
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:06:03Z
user: login: Tanujkanti4441
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-10T10:41:34Z
user: login: snitin315
body: LGTM, thanks! just a few formatting suggestions

state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T13:19:35Z
user: login: Tanujkanti4441
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T15:47:16Z
user: login: mdjermanovic
body: LGTM, thanks! Leaving open for @JoshuaKGoldberg and @snitin315 to verify.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-25T11:31:59Z
user: login: snitin315
body: LGTM, thanks!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-28T11:27:53Z

Review comments:
diff_hunk: @@ -308,6 +310,12 @@ module.exports = {
 								return;
 							}
 
+							if (
+								node.params.some(param => param.name === "this")
+							) {
+								return;
+							}
path: lib/rules/prefer-arrow-callback.js
user: login: JoshuaKGoldberg
body: [Refactor] TypeScript only allows the first param to be named `this`. JavaScript doesn't allow any params to be named `this`. This can just check the first param, if it exists.
created_at: 2025-05-02T13:32:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +110,36 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+This rule additionally supports TypeScript type syntax.
path: docs/src/rules/prefer-arrow-callback.md
user: login: JoshuaKGoldberg
body: [Docs] Can this note the `this:` behavior, since that's the reason for the new functionality in this PR?
created_at: 2025-05-02T13:33:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +110,36 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+This rule additionally supports TypeScript type syntax.
path: docs/src/rules/prefer-arrow-callback.md
user: login: Tanujkanti4441
body: Done!
created_at: 2025-05-02T15:49:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +110,36 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+This rule additionally supports TypeScript type syntax. However, it does not autofix callback `functions` that use `this` as a parameter.
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: We don't have to mention this. It's expected behavior.

```suggestion
This rule additionally supports TypeScript type syntax.
```
created_at: 2025-05-06T02:29:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +110,36 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+This rule additionally supports TypeScript type syntax. However, it does not autofix callback `functions` that use `this` as a parameter.
+
+Examples of **incorrect** TypeScript code for this rule:
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: Let's also add some examples with `allowUnboundThis` and `allowNamedFunctions` options
created_at: 2025-05-06T02:30:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +110,36 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+This rule additionally supports TypeScript type syntax. However, it does not autofix callback `functions` that use `this` as a parameter.
path: docs/src/rules/prefer-arrow-callback.md
user: login: JoshuaKGoldberg
body: Oh funny I'd suggested something opposite-ish: https://github.com/eslint/eslint/pull/19678#discussion_r2071620253

Maybe just including `this` in the code sample?
created_at: 2025-05-06T13:03:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +110,36 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+This rule additionally supports TypeScript type syntax. However, it does not autofix callback `functions` that use `this` as a parameter.
+
+Examples of **incorrect** TypeScript code for this rule:
path: docs/src/rules/prefer-arrow-callback.md
user: login: Tanujkanti4441
body: Done!
created_at: 2025-05-10T10:41:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -64,6 +64,36 @@ foo(function bar(n) { return n && n + bar(n - 1); }); // OK
 
 :::
 
+This rule additionally supports TypeScript type syntax.
+
+Examples of **incorrect** TypeScript code for this rule:
+
+::: incorrect
+
+```ts
+/*eslint prefer-arrow-callback: "error"*/
+
+foo(function bar(a:string) { a; });
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: ```suggestion
foo(function bar(a :string) { a; });
```
created_at: 2025-05-18T13:18:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -64,6 +64,36 @@ foo(function bar(n) { return n && n + bar(n - 1); }); // OK
 
 :::
 
+This rule additionally supports TypeScript type syntax.
+
+Examples of **incorrect** TypeScript code for this rule:
+
+::: incorrect
+
+```ts
+/*eslint prefer-arrow-callback: "error"*/
+
+foo(function bar(a:string) { a; });
+
+test('foo', function (this: any) {});
+```
+
+:::
+
+Examples of **correct** TypeScript code for this rule:
+
+::: correct
+
+```ts
+/*eslint prefer-arrow-callback: "error"*/
+
+foo((a:string) => a);
+
+const foo = function foo(bar:any) {};
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: ```suggestion
foo((a: string) => a);

const foo = function foo(bar: any) {};
```
created_at: 2025-05-18T13:18:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -88,6 +118,30 @@ foo(function bar() {});
 
 :::
 
+Examples of **incorrect** TypeScript code for this rule with `{ "allowNamedFunctions": true }`:
+
+::: incorrect
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
+
+foo(function(a:string) {});
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: ```suggestion
foo(function(a: string) {});
```
created_at: 2025-05-18T13:18:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -88,6 +118,30 @@ foo(function bar() {});
 
 :::
 
+Examples of **incorrect** TypeScript code for this rule with `{ "allowNamedFunctions": true }`:
+
+::: incorrect
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
+
+foo(function(a:string) {});
+```
+
+:::
+
+Examples of **correct** TypeScript code for this rule with `{ "allowNamedFunctions": true }`:
+
+::: correct
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
+
+foo(function bar(a:string) {});
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: ```suggestion
foo(function bar(a: string) {});
```
created_at: 2025-05-18T13:18:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +164,20 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+Examples of **incorrect** TypeScript code for this rule with `{ "allowUnboundThis": false }`:
+
+::: incorrect
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowUnboundThis": false } ] */
+
+foo(function(a:string) { this; });
+
+foo(function(a:string) { (() => this); });
path: docs/src/rules/prefer-arrow-callback.md
user: login: snitin315
body: ```suggestion
foo(function(a: string) { this; });

foo(function(a: string) { (() => this); });
```
created_at: 2025-05-18T13:18:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +164,20 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 
 :::
 
+Examples of **incorrect** TypeScript code for this rule with `{ "allowUnboundThis": false }`:
+
+::: incorrect
+
+```ts
+/* eslint prefer-arrow-callback: [ "error", { "allowUnboundThis": false } ] */
+
+foo(function(a:string) { this; });
+
+foo(function(a:string) { (() => this); });
path: docs/src/rules/prefer-arrow-callback.md
user: login: Tanujkanti4441
body: Done!
created_at: 2025-05-18T15:47:16Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-05-02T11:01:16Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *docs-eslint* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | f847d58465baf989bbdd48eca831416cd1bc3a7b |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/projects/docs-eslint/deploys/682c69c13ceec2000824f0ab |
author_association: NONE
user: login: mdjermanovic
created_at: 2025-05-19T11:36:55Z
body: > Added TypeScript support to `prefer-arrow-callback`. Now rule won't autofix following case:
> 
> ```ts
> declare function test(str: string, callback: any): void
> 
> test('clean', function (this: any) {
>   //...
> });
> ```

To clarify, the rule should still report an error, until `this` is actually used in the function or `this` parameter is removed?
author_association: MEMBER
user: login: Tanujkanti4441
created_at: 2025-05-20T11:39:26Z
body: > > Added TypeScript support to `prefer-arrow-callback`. Now rule won't autofix following case:
> > ```ts
> > declare function test(str: string, callback: any): void
> > 
> > test('clean', function (this: any) {
> >   //...
> > });
> > ```
> 
> To clarify, the rule should still report an error, until `this` is actually used in the function or `this` parameter is removed?

added tests for this in https://github.com/eslint/eslint/pull/19678/commits/f847d58465baf989bbdd48eca831416cd1bc3a7b.
author_association: CONTRIBUTOR