Function overloading allows declaring multiple versions of a function with different parameters and return types. Each version of the function (or signature) is called an overload.
The actual implementation of the function must handle all combinations of parameters defined in the overloads.
Basic syntax of function overloading
To create function overloads in TypeScript we must do the following
- Define the signatures of the overloaded functions.
- Provide a single implementation of the function that handles all combinations of parameters.
For example
// Overload definitions
function myFunction(param: string): string;
function myFunction(param: number): number;
// Function implementation
function myFunction(param: string | number): string | number {
// do things
return "";
}
In this example, the function myFunction
has two signatures: one that accepts a string
and another that accepts a number
. The implementation of the function handles both signatures.
If we try to call the function with a type, or a number of parameters that are not supported by the signatures, for example
myFunction(12); // this does not give an error
myFunction("12") // this does not give an error
let myObject = {};
myFunction(myObject); // this gives an error, does not accept an object
myFunction(12, 12); // this gives an error, does not accept two ints
We will receive a compiler or IDE error, such as the following:
The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
That is, when there are overloaded signatures of a function, TypeScript converts the function defining the object to private. Therefore, we cannot use it directly, and we must necessarily use one of its defined signatures
Overloading with different parameter types
Imagine a function that can add two numbers or concatenate two strings. We can use overloading to define both operations.
// Overload definitions
function combine(a: string, b: string): string;
function combine(a: number, b: number): number;
// Function implementation
function combine(a: string | number, b: string | number): string | number {
if (typeof a === "string" && typeof b === "string") {
return a + b;
} else if (typeof a === "number" && typeof b === "number") {
return a + b;
}
throw new Error("Parameter types do not match");
}
console.log(combine("Hello, ", "world")); // "Hello, world"
console.log(combine(5, 10)); // 15
Notice how the implementation of combine
accepts both string
and number
as parameters, to meet both definitions.
Alternatively, we could have used any
. Although as much as possible, for cleanliness, it is better to specify types
Overloading with different amounts of parameters
It is also possible to overload functions with different amounts of parameters. For example, a function that can take one or two parameters:
// Overload definitions
function showMessage(message: string): void;
function showMessage(message: string, times: number): void;
// Function implementation
function showMessage(message: string, times?: number): void {
if (times === undefined) {
console.log(message);
} else {
for (let i = 0; i < times; i++) {
console.log(message);
}
}
}
showMessage("Hello"); // "Hello"
showMessage("Hello", 3); // "Hello" "Hello" "Hello"
In this example, times
is an optional parameter that allows the showMessage
function to handle one or two arguments.