Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions docs/src/rules/func-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@ const foo1 = () => {};

:::

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).

Examples of **correct** TypeScript code for this rule with the default `"expression"` option:

::: correct

```ts
/*eslint func-style: ["error", "expression"]*/

function process(value: string): string;
function process(value: number): number;
function process(value: unknown) {
return value;
}
```

:::

### declaration

Examples of **incorrect** code for this rule with the `"declaration"` option:
Expand Down
43 changes: 41 additions & 2 deletions lib/rules/func-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,43 @@ module.exports = {
const { namedExports: exportFunctionStyle } = overrides;
const stack = [];

/**
* Checks if a function declaration is part of an overloaded function
* @param {ASTNode} node The function declaration node to check
* @returns {boolean} True if the function is overloaded
*/
function isOverloadedFunction(node) {
const functionName = node.id.name;

if (node.parent.type === "ExportNamedDeclaration") {
return node.parent.parent.body.some(
member =>
member.type === "ExportNamedDeclaration" &&
member.declaration?.type === "TSDeclareFunction" &&
member.declaration.id.name === functionName,
);
}

if (node.parent.type === "SwitchCase") {
return node.parent.parent.cases.some(switchCase =>
switchCase.consequent.some(
member =>
member.type === "TSDeclareFunction" &&
member.id.name === functionName,
),
);
}

return (
Array.isArray(node.parent.body) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still no test that would hit this. I think those tests should have the "expression"` option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside from labeled function declarations, which was caught by CI (hence this check), I can't think of any other scenario where node.parent.body wouldn't be an array.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests you added in 9b088a4 would hit this, but with options: ["expression"].

Another example of a function declaration where node.parent.body is not an array could be if (foo) function bar() {} (just needs sourceType: "script" to be a valid syntax).

Copy link
Contributor Author

@sethamus sethamus May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right I misunderstood your previous comments, sorry about that.

node.parent.body.some(
member =>
member.type === "TSDeclareFunction" &&
member.id.name === functionName,
)
);
}

const nodesToCheck = {
FunctionDeclaration(node) {
stack.push(false);
Expand All @@ -73,14 +110,16 @@ module.exports = {
!enforceDeclarations &&
node.parent.type !== "ExportDefaultDeclaration" &&
(typeof exportFunctionStyle === "undefined" ||
node.parent.type !== "ExportNamedDeclaration")
node.parent.type !== "ExportNamedDeclaration") &&
!isOverloadedFunction(node)
) {
context.report({ node, messageId: "expression" });
}

if (
node.parent.type === "ExportNamedDeclaration" &&
exportFunctionStyle === "expression"
exportFunctionStyle === "expression" &&
!isOverloadedFunction(node)
) {
context.report({ node, messageId: "expression" });
}
Expand Down
Loading
Loading