Reputation: 189
How to implement typescript function overload ?
interface Fn <T>{
(payload: string): T;
(payload: string, a: T): T;
}
let fn: Fn<number> = function(a: string, b: number) {
return b
}
report Type '(a: string, b: number) => string' is not assignable to type 'Fn<number>'.Vetur(2322)
Upvotes: 1
Views: 137
Reputation: 327634
In order to satisfy an interface with multiple (i.e., overloaded) call signatures, a function's implementation must be able to handle all such signatures. If fn
is a Fn<number>
, then you must be able to call it like this:
fn("someString"); // first overload
fn("someString", 1234); // second overload
But the implementation
function (a: string, b: number) {
return b
}
does not satisfy the first overload (since b
would be undefined
), and so is not assignable to Fn<number>
.
Instead, you should consider coming up with an implementation compatible with both call signatures. For example:
let fn: Fn<number> = function (a: string, b?: number) {
return b ?? a.length
}
Here, the implementation requires its first parameter a
to be a string
and takes an optional second parameter b
of type number
. Since b
might be undefined
and you have to return a number
, you can't just return b
directly. In the above I return b
unless it is undefined
, in which case I return a.length
. This is always a number
and the compiler can verify the assignment as valid.
Note: there is a complication in situations where the overloaded call signatures have different return types. Unless your function expression's implementation returns the intersection of all the return types, the compiler will not be able to verify type safety and will error, so you'll need to use a type assertion:
interface OtherFn {
(payload: string): string;
(payload: string, a: number): number;
}
let fn2: OtherFn = (a: string, b?: number) => b ?? a; // error!
// ~~~ <-- string | number not assignable to string
console.log(fn2("abc").toUpperCase()) // ABC
console.log(fn2("abc", Math.PI).toFixed(2)) // 3.14
Here you can see that fn2
does satisfy both call signatures in OtherFn
, but the compiler is unable to verify it because of the different return types. You'll need a type assertion if you want to do this with function expressions:
let fn3 = ((a: string, b?: number) => b ?? a) as OtherFn; // okay
or you can abandon function expressions and use function statements, which are checked more loosely, allowing the return type to be the union of all the call signatures return types:
function fn4(payload: string): string;
function fn4(payload: string, a: number): number;
function fn4(a: string, b?: number) {
return b ?? a;
}
which can be seen to satisfy OtherFn
despite not being manually annotated as such.
let fn5: OtherFn = fn4; // okay
Upvotes: 1
Reputation: 42160
In order for both of those overloads to apply to fn
, you have to make b
optional in your implementation. When you do that, you'll see a different error because our type says that we must return a number
, so returning b
when b
might be undefined
doesn't cut it. We can either set a default value for b
, or return some default value within the body of the function.
let fn: Fn<number> = function(a: string, b: number = 0) {
return b;
}
let fn: Fn<number> = function(a: string, b?: number) {
return b === undefined ? 0 : b;
}
Upvotes: 1