Skip to content

Commit 7cbcbf9

Browse files
authored
feat: add countThis option to max-params (#20236)
* feat: add type def * feat: implement new option * docs: update rule documentation * chore: feedbacks * test: fix * test: restore altered tests
1 parent 2bd0f13 commit 7cbcbf9

4 files changed

Lines changed: 96 additions & 28 deletions

File tree

docs/src/rules/max-params.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ This rule enforces a maximum number of parameters allowed in function definition
2929
This rule has a number or object option:
3030

3131
* `"max"` (default `3`) enforces a maximum number of parameters in function definitions
32-
* `"countVoidThis"` (default `false`) counts a `this` declaration when the type is `void` (TypeScript only)
32+
* `"countThis"` (default `"except-void"`, possible values `"always"`, `"never"` or `"except-void"`) whether to count a `this` declaration (TypeScript only)
3333

3434
**Deprecated:** The object property `maximum` is deprecated; please use the object property `max` instead.
35+
**Deprecated:** The object property `countVoidThis` is deprecated; please use `countThis: "except-void"` instead of `countVoidThis: false`, and `countThis: "always"` instead of `countVoidThis: true`.
3536

3637
### max
3738

@@ -71,16 +72,21 @@ let foo2 = (bar, baz, qux) => {
7172

7273
:::
7374

74-
### countVoidThis (TypeScript only)
75+
### countThis (TypeScript only)
7576

76-
This rule has a TypeScript-specific option `countVoidThis` that allows you to count a `this` declaration when the type is `void`.
77+
This rule has a TypeScript-specific option `countThis` that allows you to count a `this` declaration.
78+
Valid values are:
7779

78-
Examples of **correct** TypeScript code for this rule with the default `{ "countVoidThis": false }` option:
80+
* `"always"`: any `this` type annotation will count as a parameter
81+
* `"never"`: `this` won't ever be counted
82+
* `"except-void"`: `this` will be counted only if its type is not `void`. Equivalent to the deprecated `countVoidThis: false`.
83+
84+
Examples of **correct** TypeScript code for this rule:
7985

8086
:::correct
8187

8288
```ts
83-
/*eslint max-params: ["error", { "max": 2, "countVoidThis": false }]*/
89+
/*eslint max-params: ["error", { "max": 2, "countThis": "except-void" }]*/
8490

8591
function hasNoThis(this: void, first: string, second: string) {
8692
// ...
@@ -89,42 +95,38 @@ function hasNoThis(this: void, first: string, second: string) {
8995

9096
:::
9197

92-
Examples of **incorrect** TypeScript code for this rule with the default `{ "countVoidThis": false }` option:
93-
94-
:::incorrect
98+
:::correct
9599

96100
```ts
97-
/*eslint max-params: ["error", { "max": 2, "countVoidThis": false }]*/
101+
/*eslint max-params: ["error", { "max": 2, "countThis": "never" }]*/
98102

99-
function hasNoThis(this: void, first: string, second: string, third: string) {
103+
function hasThis(this: unknown[], first: string, second: string) {
100104
// ...
101105
}
102106
```
103107

104108
:::
105109

106-
Examples of **correct** TypeScript code for this rule with the `{ "countVoidThis": true }` option:
110+
Examples of **incorrect** TypeScript code for this rule:
107111

108-
:::correct
112+
:::incorrect
109113

110114
```ts
111-
/*eslint max-params: ["error", { "max": 2, "countVoidThis": true }]*/
115+
/*eslint max-params: ["error", { "max": 2, "countThis": "always" }]*/
112116

113-
function hasNoThis(this: void, first: string) {
117+
function hasNoThis(this: void, first: string, second: string) {
114118
// ...
115119
}
116120
```
117121

118122
:::
119123

120-
Examples of **incorrect** TypeScript code for this rule with the `{ "countVoidThis": true }` option:
121-
122124
:::incorrect
123125

124126
```ts
125-
/*eslint max-params: ["error", { "max": 2, "countVoidThis": true }]*/
127+
/*eslint max-params: ["error", { "max": 2, "countThis": "except-void" }]*/
126128

127-
function hasNoThis(this: void, first: string, second: string) {
129+
function hasThis(this: unknown[], first: string, second: string) {
128130
// ...
129131
}
130132
```

lib/rules/max-params.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,14 @@ module.exports = {
5353
description:
5454
"Whether to count a `this` declaration when the type is `void`.",
5555
},
56+
countThis: {
57+
enum: ["never", "except-void", "always"],
58+
description:
59+
"Whether to count a `this` declaration.",
60+
},
5661
},
5762
additionalProperties: false,
63+
not: { required: ["countVoidThis", "countThis"] },
5864
},
5965
],
6066
},
@@ -68,7 +74,7 @@ module.exports = {
6874
const sourceCode = context.sourceCode;
6975
const option = context.options[0];
7076
let numParams = 3;
71-
let countVoidThis = false;
77+
let countThis = "except-void";
7278

7379
if (typeof option === "object") {
7480
if (
@@ -77,7 +83,12 @@ module.exports = {
7783
) {
7884
numParams = option.maximum || option.max;
7985
}
80-
countVoidThis = option.countVoidThis;
86+
87+
countThis = option.countThis;
88+
if (!countThis && Object.hasOwn(option, "countVoidThis")) {
89+
countThis = option.countVoidThis ? "always" : "except-void";
90+
}
91+
countThis ||= "except-void";
8192
}
8293
if (typeof option === "number") {
8394
numParams = option;
@@ -90,17 +101,25 @@ module.exports = {
90101
* @private
91102
*/
92103
function checkFunction(node) {
93-
const hasVoidThisParam =
104+
const thisParam =
94105
node.params.length > 0 &&
95106
node.params[0].type === "Identifier" &&
96-
node.params[0].name === "this" &&
97-
node.params[0].typeAnnotation?.typeAnnotation.type ===
98-
"TSVoidKeyword";
107+
node.params[0].name === "this"
108+
? node.params[0]
109+
: null;
99110

100-
const effectiveParamCount =
101-
hasVoidThisParam && !countVoidThis
102-
? node.params.length - 1
103-
: node.params.length;
111+
let effectiveParamCount = node.params.length;
112+
if (thisParam) {
113+
if (countThis === "never") {
114+
effectiveParamCount = node.params.length - 1;
115+
} else if (
116+
countThis === "except-void" &&
117+
thisParam.typeAnnotation?.typeAnnotation.type ===
118+
"TSVoidKeyword"
119+
) {
120+
effectiveParamCount = node.params.length - 1;
121+
}
122+
}
104123

105124
if (effectiveParamCount > numParams) {
106125
context.report({

lib/types/rules.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,9 +1858,14 @@ export interface ESLintRules extends Linter.RulesRecord {
18581858
*/
18591859
max: number;
18601860
/**
1861+
* @deprecated Replaced with `countThis'
18611862
* @default false
18621863
*/
18631864
countVoidThis: boolean;
1865+
/**
1866+
* @default "except-void"
1867+
*/
1868+
countThis: "always" | "never" | "except-void";
18641869
}>,
18651870
]
18661871
>;

tests/lib/rules/max-params.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,14 @@ ruleTesterTypeScript.run("max-params", rule, {
232232
`,
233233
options: [{ max: 2 }],
234234
},
235+
{
236+
code: `function foo(this: unknown[], a, b, c) {}`,
237+
options: [{ max: 3, countThis: "never" }],
238+
},
239+
{
240+
code: `function foo(this: void, a, b, c) {}`,
241+
options: [{ max: 3, countThis: "except-void" }],
242+
},
235243
],
236244
invalid: [
237245
{
@@ -268,11 +276,35 @@ ruleTesterTypeScript.run("max-params", rule, {
268276
options: [{ countVoidThis: true, max: 1 }],
269277
errors: [{ messageId: "exceed" }],
270278
},
279+
{
280+
code: `
281+
class Foo {
282+
method(this: void, a) {}
283+
}
284+
`,
285+
options: [{ countThis: "always", max: 1 }],
286+
errors: [{ messageId: "exceed" }],
287+
},
271288
{
272289
code: `function testD(this: void, a) {}`,
273290
options: [{ countVoidThis: true, max: 1 }],
274291
errors: [{ messageId: "exceed" }],
275292
},
293+
{
294+
code: `function testD(this: void, a) {}`,
295+
options: [{ countThis: "always", max: 1 }],
296+
errors: [{ messageId: "exceed" }],
297+
},
298+
{
299+
code: `const testE = function (this: void, a) {}`,
300+
options: [{ countThis: "always", max: 1 }],
301+
errors: [{ messageId: "exceed" }],
302+
},
303+
{
304+
code: `function testFunction(test: void, a: number) {}`,
305+
options: [{ countThis: "except-void", max: 1 }],
306+
errors: [{ messageId: "exceed" }],
307+
},
276308
{
277309
code: `const testE = function (this: void, a) {}`,
278310
options: [{ countVoidThis: true, max: 1 }],
@@ -305,5 +337,15 @@ ruleTesterTypeScript.run("max-params", rule, {
305337
options: [{ max: 1 }],
306338
errors: [{ messageId: "exceed" }],
307339
},
340+
{
341+
code: `function foo(this: unknown[], a, b, c) {}`,
342+
options: [{ max: 3, countThis: "always" }],
343+
errors: [{ messageId: "exceed" }],
344+
},
345+
{
346+
code: `function foo(this: unknown[], a, b, c) {}`,
347+
options: [{ max: 3, countThis: "except-void" }],
348+
errors: [{ messageId: "exceed" }],
349+
},
308350
],
309351
});

0 commit comments

Comments
 (0)