unified-signatures
Disallow two overloads that could be unified into one with a union or an optional/rest parameter.
Extending "plugin:@typescript-eslint/strict" in an ESLint configuration enables this rule.
Function overload signatures are a TypeScript way to define a function that can be called in multiple very different ways. Overload signatures add syntax and theoretical bloat, so it's generally best to avoid using them when possible. Switching to union types and/or optional or rest parameters can often avoid the need for overload signatures.
This rule reports when function overload signatures can be replaced by a single function signature.
- Flat Config
- Legacy Config
export default defineConfig({
rules: {
"@typescript-eslint/unified-signatures": "error"
}
});
module.exports = {
"rules": {
"@typescript-eslint/unified-signatures": "error"
}
};
Try this rule in the playground ↗
Examples
- ❌ Incorrect
- ✅ Correct
function x(y: number): void;
function x(y: string): void;
Open in Playgroundfunction y(): void;
function y(...x: number[]): void;
Open in Playgroundfunction x(y: number | string): void;
Open in Playgroundfunction y(...x: number[]): void;
Open in Playground// This rule won't check overload signatures with different rest parameter types.
// See https://github.com/microsoft/TypeScript/issues/5077
function f(...a: number[]): void;
function f(...a: string[]): void;
Open in PlaygroundOptions
This rule accepts the following options:
type Options = [
{
/** Whether two parameters with different names at the same index should be considered different even if their types are the same. */
ignoreDifferentlyNamedParameters?: boolean;
/** Whether two overloads with different JSDoc comments should be considered different even if their parameter and return types are the same. */
ignoreOverloadsWithDifferentJSDoc?: boolean;
},
];
const defaultOptions: Options = [
{
ignoreDifferentlyNamedParameters: false,
ignoreOverloadsWithDifferentJSDoc: false,
},
];
ignoreDifferentlyNamedParameters
Whether two parameters with different names at the same index should be considered different even if their types are the same. Default: false.
Examples of code for this rule with ignoreDifferentlyNamedParameters:
- ❌ Incorrect
- ✅ Correct
function f(a: number): void;
function f(a: string): void;
Open in Playgroundfunction f(a: number): void;
function f(b: string): void;
Open in PlaygroundignoreOverloadsWithDifferentJSDoc
Whether two overloads with different JSDoc comments should be considered different even if their parameter and return types are the same. Default: false.
Examples of code for this rule with ignoreOverloadsWithDifferentJSDoc:
- ❌ Incorrect
- ✅ Correct
declare function f(x: string): void;
declare function f(x: boolean): void;
/**
* @deprecate
*/
declare function f(x: number): void;
/**
* @deprecate
*/
declare function f(x: null): void;
Open in Playgrounddeclare function f(x: string): void;
/**
* This signature does something else.
*/
declare function f(x: boolean): void;
/**
* @async
*/
declare function f(x: number): void;
/**
* @deprecate
*/
declare function f(x: null): void;
Open in PlaygroundCaveats
This rule reports overloads that have the same shape, so they could be written as a single signature. It does not check whether the merged signature would accept exactly the same calls as the separate overloads. There is intentionally no autofixer: it's up to you to decide whether merging is appropriate.
Merging is most often not equivalent when the differing parameters are object types, because of excess property checking. An object literal passed to separate overloads must match one of them exactly, whereas a union parameter permits properties from any of its members - including a literal that mixes properties from several:
interface A {
a: string;
}
interface B {
b: string;
}
declare function separate(x: A): void;
declare function separate(x: B): void;
// ~~~~ These overloads can be combined into one signature taking `A | B`.
declare function merged(x: A | B): void;
separate({ a: '', b: '' }); // Error: No overload matches this call.
merged({ a: '', b: '' }); // No error
Open in PlaygroundIf merging would change the types your function accepts, add an ESLint disable comment explaining why the overloads must stay separate.
When Not To Use It
This is purely a stylistic rule to help with readability of function signature overloads. You can turn it off if you don't want to consistently keep them next to each other and unified.