"Overloads are for when one function is really several functions wearing the same hat."
The shape of an overload
A function overload is a way to give a single function multiple call signatures. You write the overload signatures first (the public contract), then a single implementation signature that accepts a union of everything. The implementation signature is NOT directly callable — only the overload signatures are.
function format(value: number): string;
function format(value: Date): string;
function format(value: number | Date): string {
// implementation handles both
return value instanceof Date ? value.toISOString() : value.toFixed(2);
}
Callers see two distinct signatures: pass a number or a Date, get a string. The compiler routes the call to the implementation. The implementation does runtime checks to handle both inputs.
When overloads are the right tool
Use overloads when the function genuinely has multiple, distinct call shapes AND a single signature (with union types, generics, or conditional types) doesn't express the relationship cleanly.
Counter-examples — places where a single signature wins:
- Same parameter, multiple types:
function format(value: number | Date): string— union, no overload needed. - Type-dependent return:
function wrap<T>(x: T): { value: T }— generic, no overload needed. - Conditional return based on input:
function f<T>(x: T): T extends string ? number : string— conditional type, no overload needed.
Where overloads do shine: when call shapes have different parameter counts, or when distinct call shapes need distinct documentation in IDE hover.
The implementation-signature pitfall
The implementation signature is private — callers can't see it. But it must accept the union of all overload signatures. If you write four overloads but a narrow implementation, the compiler complains.
Worse: the implementation signature is checked for compatibility but is NOT part of the public type. Even if it accepts any, callers can only use the public signatures. This catches developers who assume "my implementation takes any, so this call works."