What is function overloading in TypeScript?
Function overloading allows a function to accept different types or numbers of arguments, and potentially return different types, based on the specific call signature. In TypeScript, this is achieved by defining multiple function signatures (overload signatures) for a single function implementation, enabling type-safe calls while maintaining a single, unified logic.
What is Function Overloading?
Function overloading is a feature that enables you to define multiple distinct call signatures for a function, but only one actual implementation of the function. This means that a single function name can be used to perform different actions or handle different types of input, depending on how it's called. The compiler uses the provided signatures to determine which specific version of the function is being invoked and to provide appropriate type checking and IntelliSense.
How TypeScript Implements It
TypeScript implements function overloading using a combination of 'overload signatures' and an 'implementation signature'.
- Overload Signatures: These are the public-facing function declarations that specify the different ways a function can be called. They define the types of parameters and the return type for each specific use case. These signatures are used by the TypeScript compiler to check calls made to the function.
- Implementation Signature: This is the actual function definition that contains the executable code. It must have a signature that is compatible with (i.e., encompasses or is broader than) all the overload signatures. The parameters in the implementation signature often use union types or optional parameters to accommodate all possible inputs defined by the overload signatures. The implementation signature itself is not directly callable from outside the function; it exists to provide the logic and to ensure type safety internally.
Example
Consider a function that can either concatenate two strings or add two numbers. We can overload this function to provide specific type checking for both scenarios.
function processInput(a: string, b: string): string;
function processInput(a: number, b: number): number;
function processInput(a: string | number, b: string | number): string | number {
if (typeof a === 'string' && typeof b === 'string') {
return a + b; // Concatenates strings
} else if (typeof a === 'number' && typeof b === 'number') {
return a + b; // Adds numbers
}
throw new Error('Invalid input types');
}
// Usage with type checking:
const result1 = processInput('Hello', ' World'); // result1 is inferred as string
const result2 = processInput(10, 20); // result2 is inferred as number
console.log(result1); // "Hello World"
console.log(result2); // 30
// This will cause a compile-time error due to type mismatch based on overloads:
// const result3 = processInput('Hello', 10);
In this example, we define two overload signatures for processInput: one for string parameters returning a string, and one for number parameters returning a number. The actual function implementation processInput(a: string | number, b: string | number): string | number uses union types to cover both cases. When calling processInput, TypeScript matches the arguments against the overload signatures to determine the correct return type and to enforce type safety.