TIL: TypeScript Function Overloads
Today I learned about function overloads in TypeScript. They solve a common problem - making your code understand exactly what type comes out based on what type goes in.
What Function Overloads Do
Function overloads let me define multiple ways to call the same function:
// These tell TypeScript about different ways to call my function
function function convert(value: string): number (+1 overload)
convert(value: string
value: string): number;
function function convert(value: number): string (+1 overload)
convert(value: number
value: number): string;
// This is the actual function that runs
function function convert(value: string): number (+1 overload)
convert(value: string | number
value: string | number): string | number {
if (typeof value: string | number
value === "string") {
return function parseInt(string: string, radix?: number): number
Converts a string to an integer.parseInt(value: string
value);
} else {
return value: number
value.Number.toString(radix?: number): string
Returns a string representation of an object.toString();
}
}
const const a: number
a = function convert(value: string): number (+1 overload)
convert("42"); // TypeScript knows this is a number
const const b: string
b = function convert(value: number): string (+1 overload)
convert(42); // TypeScript knows this is a string
How This Improves My Code
Without overloads, I’d have to use a union type, which loses information:
function function convert(value: string | number): string | number
convert(value: string | number
value: string | number): string | number {
if (typeof value: string | number
value === "string") {
return function parseInt(string: string, radix?: number): number
Converts a string to an integer.parseInt(value: string
value);
} else {
return value: number
value.Number.toString(radix?: number): string
Returns a string representation of an object.toString();
}
}
const const a: string | number
a = function convert(value: string | number): string | number
convert("42"); // TypeScript only knows this is string | number
const const b: string | number
b = function convert(value: string | number): string | number
convert(42); // TypeScript only knows this is string | number
With overloads, TypeScript remembers which type I get back based on what I put in!
Overloads vs. Union Types
- Overloads: More precise typing, TypeScript knows exactly which output type matches which input type
- Union types: Simpler code, but TypeScript only knows all possible output types, not which specific one
Limitations in TypeScript
The big drawback? In TypeScript, I can’t write separate code for each overload. I write one function that handles all cases. This gets messy with complex functions.
C++ does this better. In C++, each overload is a completely separate function:
// Two totally separate functions
void convert(int value) {
// Code for integers only
cout << to_string(value) << endl;
}
void convert(string value) {
// Different code for strings only
cout << stoi(value) << endl;
}
In TypeScript, I have to write my own type-checking logic to figure out which version was called. This makes overloads in TypeScript harder to use for complex cases.