Difficulty: Intermediate
Function overloads in TypeScript allow a single function to have multiple call signatures - different combinations of parameter types and return types. This is useful when a function's return type depends on the types of its arguments. For example, a `parse` function might return a `number` when given a string that looks numeric, but a `Date` when given a date string. Overloads let you express these relationships precisely.
The syntax involves writing one or more overload signatures (declarations without a body) followed by the implementation signature (with the body). The overload signatures define what callers see, while the implementation signature defines how the function works internally. The implementation must be compatible with all overload signatures but is not directly visible to callers. Only the overload signatures appear in editor tooltips and documentation.
TypeScript resolves overloads in order from top to bottom. When you call an overloaded function, TypeScript checks each overload signature until it finds one that matches the arguments. This means you should place more specific signatures before more general ones. If a less specific signature appears first, it will match before the specific one gets checked.
While overloads are powerful, they should be used judiciously. In many cases, union types or generics provide a simpler solution. Use overloads when the return type genuinely varies based on the input type in a way that unions can't express. If a function returns `string | number` regardless of input, a simple union return type is clearer than overloads. The rule of thumb: use overloads when there's a meaningful correlation between specific input types and specific output types.
// Overload signatures
function format(value: string): string;
function format(value: number): string;
function format(value: boolean): string;
// Implementation signature
function format(value: string | number | boolean): string {
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value === "number") {
return value.toFixed(2);
}
return value ? "TRUE" : "FALSE";
}
console.log(format("hello"));
console.log(format(42));
console.log(format(true));
Each overload signature defines one valid way to call the function. The implementation handles all cases but callers only see the overload signatures. TypeScript ensures the call matches one of the declared overloads.
// Return type depends on input type
function parse(value: string, type: "number"): number;
function parse(value: string, type: "boolean"): boolean;
function parse(value: string, type: "json"): object;
function parse(value: string, type: string): number | boolean | object {
switch (type) {
case "number":
return parseFloat(value);
case "boolean":
return value === "true";
case "json":
return JSON.parse(value);
default:
throw new Error(`Unknown type: ${type}`);
}
}
const num = parse("3.14", "number"); // TypeScript knows: number
const bool = parse("true", "boolean"); // TypeScript knows: boolean
const obj = parse('{"a":1}', "json"); // TypeScript knows: object
console.log(num, typeof num);
console.log(bool, typeof bool);
console.log(obj);
The real power of overloads: TypeScript knows the exact return type based on which overload matches. `parse(x, 'number')` returns `number`, not `number | boolean | object`.
// OVERLOADS - not needed here, union is simpler
// function getLength(value: string): number;
// function getLength(value: unknown[]): number;
// function getLength(value: string | unknown[]): number { ... }
// UNION - simpler and clearer for the same result
function getLength(value: string | unknown[]): number {
return value.length;
}
// Both string and arrays have .length, so a union type works perfectly
console.log(getLength("hello"));
console.log(getLength([1, 2, 3, 4]));
console.log(getLength([]));
When the return type is the same regardless of input type, union parameters are simpler than overloads. Reserve overloads for when the return type correlates with specific input types.
Overload signatures, Implementation signature, Call signature resolution, When to use overloads