Function overloads
Several signatures for one function: the result type depends on which arguments you call it with.
Example
#function toPoints(value: string): number
function toPoints(values: string[]): number[]
function toPoints(input: string | string[]): number | number[] {
if (Array.isArray(input)) return input.map((item) => Number.parseInt(item, 10))
return Number.parseInt(input, 10)
}
const single = toPoints('120')
// const single: number
const many = toPoints(['40', '75', '90'])
// const many: number[]
console.log(single + 30)
console.log(many)150 [40, 75, 90]
Definition and usage
#Overloads describe one function with several signatures. You first list the headers alone, with no body, and directly below them a single implementation. At the call site, the compiler checks the headers from top to bottom and picks the first one that matches the arguments. That lets a function return a number for a string, and an array of numbers for an array of strings.
The implementation’s signature is hidden: from the outside, only the overloads are visible. The implementation has to handle every variant, so it usually accepts a union type and narrows it itself inside, for example with Array.isArray() or typeof. If some header doesn’t match it, the compiler reports “This overload signature is not compatible with its implementation signature.”
You can also overload class methods, and in interfaces it’s enough to write several call signatures. Before reaching for overloads, though, check whether a union, an optional parameter, or a generic would do. Overloads pay off when the result type depends on the arguments, or when the variants differ in the number and meaning of their parameters.
Syntax
#function name(a: TypeA): ResultA
function name(a: TypeB): ResultB
function name(a: TypeA | TypeB): ResultA | ResultB {
// implementation
}More examples
#function makeRange(end: number): number[]
function makeRange(start: number, end: number): number[]
function makeRange(a: number, b?: number): number[] {
const [start, end] = b === undefined ? [0, a] : [a, b]
return Array.from({ length: end - start }, (_, i) => start + i)
}
console.log(makeRange(4))
console.log(makeRange(2, 5))
makeRange(1, 2, 3)
// Error: Expected 1-2 arguments, but got 3.[0, 1, 2, 3] [2, 3, 4]
toPoints(42)
// Error: No overload matches this call.
// Overload 1 of 2, '(value: string): number', gave the following error.
// Argument of type 'number' is not assignable to parameter of type 'string'.
// Overload 2 of 2, '(values: string[]): number[]', gave the following error.
// Argument of type 'number' is not assignable to parameter of type 'string[]'.Good practices
#- Order overloads from most specific to most general. The compiler takes the first match, so a general signature on top would shadow the rest.
- A union-typed argument, like
string | string[], doesn’t match any of the overloads in the example, because the implementation’s signature isn’t visible. If calls like that are needed, add a separate overload that accepts the union. - When every variant returns the same type and differs only in the parameter type, replace the overloads with a single signature using a union. The code will be shorter, and the editor’s suggestions simpler.
Related entries
#Spotted a mistake or missing an example? Write to us.