I got a little confused by the official TypeScript documentation, particularily the Conditional Types page. There's this snippet in the docs:
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}
function createLabel(id: number): IdLabel;
function createLabel(name: string): NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel; // Why?
function createLabel(nameOrId: string | number): IdLabel | NameLabel {
throw "unimplemented";
}
I don't quite get it why would they include the third overload, which is identical to final function signature? Is it an overlook, or does it serve a purpose?
TypeScript differentiates between two types of signatures whenever a function uses overloads. The first type being Overload Signatures which describe all possible ways to call a function and the second one being the Implementation Signature which is used to write the body of the function.
The Implementation Signature is not considered as a valid call signature. It is only used to provide parameter typings for the implementation of the function.
The third overload of your example is necessary to make the function callable with a string | number union.
function createLabel(id: number): IdLabel;
function createLabel(name: string): NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel {
throw "unimplemented";
}
createLabel(Math.random() > 0.5 ? "abc" : 123)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Argument of type 'string | number'
// is not assignable to parameter
// of type 'string'
The compiler would prevent us from calling the function with a union; even if the argument type was compatible with the implementation signature.
Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With