Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5117057
feat: port rule @typescript-eslint/max-params
ScriptedAlchemy Feb 20, 2026
703e7a6
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Feb 25, 2026
94ffd14
fix(typescript/max-params): align signature node parity
ScriptedAlchemy Feb 25, 2026
536b1db
fix: support max-params shorthand options
ScriptedAlchemy Feb 26, 2026
e45bebe
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 3, 2026
e3207fc
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 4, 2026
81aeb77
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 5, 2026
5ce5760
Merge remote-tracking branch 'origin/main' into codex/ts-eslint-max-p…
ScriptedAlchemy Mar 11, 2026
a30a61d
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 11, 2026
5fb9856
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 12, 2026
4c41a9a
Merge remote-tracking branch 'origin/main' into codex/ts-eslint-max-p…
ScriptedAlchemy Mar 12, 2026
06ebc85
fix: cover ts signature nodes in max-params
ScriptedAlchemy Mar 12, 2026
bc7530d
Merge remote-tracking branch 'origin/main' into codex/ts-eslint-max-p…
ScriptedAlchemy Mar 12, 2026
566c0ab
Merge remote-tracking branch 'origin/main' into codex/ts-eslint-max-p…
ScriptedAlchemy Mar 12, 2026
999cf30
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 12, 2026
c2c1093
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 12, 2026
d4d2968
Merge remote-tracking branch 'origin/codex/ts-eslint-max-params-ac37'…
ScriptedAlchemy Mar 13, 2026
066aabd
fix: align max-params diagnostics with upstream
ScriptedAlchemy Mar 13, 2026
d7473aa
Merge remote-tracking branch 'origin/main' into codex/ts-eslint-max-p…
ScriptedAlchemy Mar 16, 2026
0826bbf
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 17, 2026
98a7de3
Merge remote-tracking branch 'origin/main' into codex/ts-eslint-max-p…
ScriptedAlchemy Mar 19, 2026
a63a0ec
Merge branch 'main' into codex/ts-eslint-max-params-ac37
ScriptedAlchemy Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_type_imports"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/default_param_last"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/dot_notation"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/max_params"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_array_constructor"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_array_delete"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_base_to_string"
Expand Down Expand Up @@ -350,6 +351,7 @@ func registerAllTypeScriptEslintPluginRules() {
GlobalRuleRegistry.Register("@typescript-eslint/consistent-type-imports", consistent_type_imports.ConsistentTypeImportsRule)
GlobalRuleRegistry.Register("@typescript-eslint/default-param-last", default_param_last.DefaultParamLastRule)
GlobalRuleRegistry.Register("@typescript-eslint/dot-notation", dot_notation.DotNotationRule)
GlobalRuleRegistry.Register("@typescript-eslint/max-params", max_params.MaxParamsRule)
GlobalRuleRegistry.Register("@typescript-eslint/no-array-constructor", no_array_constructor.NoArrayConstructorRule)
GlobalRuleRegistry.Register("@typescript-eslint/no-array-delete", no_array_delete.NoArrayDeleteRule)
GlobalRuleRegistry.Register("@typescript-eslint/no-base-to-string", no_base_to_string.NoBaseToStringRule)
Expand Down
207 changes: 207 additions & 0 deletions internal/plugins/typescript/rules/max_params/max_params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package max_params

import (
"fmt"

"github.com/microsoft/typescript-go/shim/ast"
"github.com/web-infra-dev/rslint/internal/rule"
)

type MaxParamsOptions struct {
Max int `json:"max"`
CountVoidThis bool `json:"countVoidThis"`
}

func parseNumericOption(value interface{}) (int, bool) {
switch v := value.(type) {
case int:
return v, true
case int32:
return int(v), true
case int64:
return int(v), true
case float32:
return int(v), true
case float64:
return int(v), true
default:
return 0, false
}
}

func parseOptions(options any) MaxParamsOptions {
opts := MaxParamsOptions{
Max: 3,
CountVoidThis: false,
}

if options == nil {
return opts
}

var optsMap map[string]interface{}
if arr, ok := options.([]interface{}); ok && len(arr) > 0 {
first := arr[0]
if maxValue, ok := parseNumericOption(first); ok {
opts.Max = maxValue
return opts
}
if m, ok := first.(map[string]interface{}); ok {
optsMap = m
}
} else if maxValue, ok := parseNumericOption(options); ok {
opts.Max = maxValue
return opts
} else if m, ok := options.(map[string]interface{}); ok {
optsMap = m
}

if optsMap == nil {
return opts
}

hasMax := false
if value, ok := optsMap["max"]; ok {
if maxValue, ok := parseNumericOption(value); ok {
opts.Max = maxValue
hasMax = true
}
}
if !hasMax {
if value, ok := optsMap["maximum"]; ok {
if maxValue, ok := parseNumericOption(value); ok {
opts.Max = maxValue
}
}
}
if value, ok := optsMap["countVoidThis"]; ok {
if flag, ok := value.(bool); ok {
opts.CountVoidThis = flag
}
}

return opts
}

func isVoidThisParameter(param *ast.Node) bool {
if param == nil || !ast.IsParameter(param) {
return false
}

decl := param.AsParameterDeclaration()
if decl == nil {
return false
}

name := decl.Name()
if name == nil || name.Kind != ast.KindIdentifier {
return false
}

if name.AsIdentifier().Text != "this" {
return false
}

return decl.Type != nil && decl.Type.Kind == ast.KindVoidKeyword
}

func getNamedFunctionLabel(prefix string, nameNode *ast.Node) string {
if nameNode != nil && nameNode.Kind == ast.KindIdentifier {
return fmt.Sprintf("%s '%s'", prefix, nameNode.AsIdentifier().Text)
}
return prefix
}

func getFunctionLabel(node *ast.Node) string {
switch node.Kind {
case ast.KindFunctionDeclaration:
if decl := node.AsFunctionDeclaration(); decl != nil {
return getNamedFunctionLabel("Function", decl.Name())
}
return "Function"
case ast.KindFunctionExpression:
if expr := node.AsFunctionExpression(); expr != nil {
return getNamedFunctionLabel("Function", expr.Name())
}
return "Function"
case ast.KindArrowFunction:
return "Arrow function"
case ast.KindMethodDeclaration:
if decl := node.AsMethodDeclaration(); decl != nil {
return getNamedFunctionLabel("Method", decl.Name())
}
return "Method"
case ast.KindMethodSignature:
if sig := node.AsMethodSignatureDeclaration(); sig != nil {
return getNamedFunctionLabel("Method", sig.Name())
}
return "Method"
case ast.KindConstructor:
return "Constructor"
case ast.KindGetAccessor:
if accessor := node.AsGetAccessorDeclaration(); accessor != nil {
return getNamedFunctionLabel("Getter", accessor.Name())
}
return "Getter"
case ast.KindSetAccessor:
if accessor := node.AsSetAccessorDeclaration(); accessor != nil {
return getNamedFunctionLabel("Setter", accessor.Name())
}
return "Setter"
case ast.KindFunctionType:
return "Function type"
case ast.KindCallSignature:
return "Call signature"
case ast.KindConstructSignature:
return "Constructor signature"
case ast.KindConstructorType:
return "Constructor type"
default:
return "Function"
}
}

func buildExceedMessage(node *ast.Node, count int, maxCount int) rule.RuleMessage {
return rule.RuleMessage{
Id: "exceed",
Description: fmt.Sprintf("%s has too many parameters (%d). Maximum allowed is %d.", getFunctionLabel(node), count, maxCount),
}
}

var MaxParamsRule = rule.CreateRule(rule.Rule{
Name: "max-params",
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
opts := parseOptions(options)

checkParameters := func(node *ast.Node) {
params := node.Parameters()
if params == nil {
return
}

count := len(params)
if !opts.CountVoidThis && count > 0 && isVoidThisParameter(params[0]) {
count--
}

if count > opts.Max {
ctx.ReportNode(node, buildExceedMessage(node, count, opts.Max))
}
}

return rule.RuleListeners{
ast.KindFunctionDeclaration: checkParameters,
ast.KindFunctionExpression: checkParameters,
ast.KindArrowFunction: checkParameters,
ast.KindMethodDeclaration: checkParameters,
ast.KindMethodSignature: checkParameters,
ast.KindCallSignature: checkParameters,
ast.KindConstructSignature: checkParameters,
ast.KindConstructorType: checkParameters,
ast.KindConstructor: checkParameters,
ast.KindGetAccessor: checkParameters,
ast.KindSetAccessor: checkParameters,
ast.KindFunctionType: checkParameters,
}
},
})
42 changes: 42 additions & 0 deletions internal/plugins/typescript/rules/max_params/max_params.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# max-params

## Rule Details

Enforce a maximum number of parameters in function-like declarations.

Examples of **incorrect** code for this rule:

```typescript
function foo(a, b, c, d) {}

class Foo {
method(this: Foo, a, b, c) {}
}
```

Examples of **correct** code for this rule:

```typescript
function foo(a, b, c) {}

class Foo {
method(this: void, a, b, c) {}
}
```

## Options

- `max` (`number`, default: `3`): Maximum number of parameters.
- `maximum` (`number`, deprecated): Alias of `max`.
- `countVoidThis` (`boolean`, default: `false`): When `false`, a leading `this: void` parameter is not counted.

The numeric shorthand is also supported:

```typescript
// equivalent to { max: 4 }
['@typescript-eslint/max-params', 2, 4];
```

## Original Documentation

https://typescript-eslint.io/rules/max-params
Loading
Loading