Skip to content

Commit bc3c331

Browse files
authored
feat: ignore overloaded function declarations in func-style rule (#19755)
* feat: ignore overloaded function declarations in func-style rule * fix CI * refactor * add more tests * refactor * add tests
1 parent 5bc21f9 commit bc3c331

File tree

3 files changed

+564
-2
lines changed

3 files changed

+564
-2
lines changed

docs/src/rules/func-style.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,24 @@ const foo1 = () => {};
104104

105105
:::
106106

107+
Overloaded function declarations are not reported as errors by this rule. These are functions that have multiple declarations with the same name but different parameter types or return types (commonly used in TypeScript to provide type information for different ways of calling the same function).
108+
109+
Examples of **correct** TypeScript code for this rule with the default `"expression"` option:
110+
111+
::: correct
112+
113+
```ts
114+
/*eslint func-style: ["error", "expression"]*/
115+
116+
function process(value: string): string;
117+
function process(value: number): number;
118+
function process(value: unknown) {
119+
return value;
120+
}
121+
```
122+
123+
:::
124+
107125
### declaration
108126

109127
Examples of **incorrect** code for this rule with the `"declaration"` option:

lib/rules/func-style.js

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,43 @@ module.exports = {
6565
const { namedExports: exportFunctionStyle } = overrides;
6666
const stack = [];
6767

68+
/**
69+
* Checks if a function declaration is part of an overloaded function
70+
* @param {ASTNode} node The function declaration node to check
71+
* @returns {boolean} True if the function is overloaded
72+
*/
73+
function isOverloadedFunction(node) {
74+
const functionName = node.id.name;
75+
76+
if (node.parent.type === "ExportNamedDeclaration") {
77+
return node.parent.parent.body.some(
78+
member =>
79+
member.type === "ExportNamedDeclaration" &&
80+
member.declaration?.type === "TSDeclareFunction" &&
81+
member.declaration.id.name === functionName,
82+
);
83+
}
84+
85+
if (node.parent.type === "SwitchCase") {
86+
return node.parent.parent.cases.some(switchCase =>
87+
switchCase.consequent.some(
88+
member =>
89+
member.type === "TSDeclareFunction" &&
90+
member.id.name === functionName,
91+
),
92+
);
93+
}
94+
95+
return (
96+
Array.isArray(node.parent.body) &&
97+
node.parent.body.some(
98+
member =>
99+
member.type === "TSDeclareFunction" &&
100+
member.id.name === functionName,
101+
)
102+
);
103+
}
104+
68105
const nodesToCheck = {
69106
FunctionDeclaration(node) {
70107
stack.push(false);
@@ -73,14 +110,16 @@ module.exports = {
73110
!enforceDeclarations &&
74111
node.parent.type !== "ExportDefaultDeclaration" &&
75112
(typeof exportFunctionStyle === "undefined" ||
76-
node.parent.type !== "ExportNamedDeclaration")
113+
node.parent.type !== "ExportNamedDeclaration") &&
114+
!isOverloadedFunction(node)
77115
) {
78116
context.report({ node, messageId: "expression" });
79117
}
80118

81119
if (
82120
node.parent.type === "ExportNamedDeclaration" &&
83-
exportFunctionStyle === "expression"
121+
exportFunctionStyle === "expression" &&
122+
!isOverloadedFunction(node)
84123
) {
85124
context.report({ node, messageId: "expression" });
86125
}

0 commit comments

Comments
 (0)