如何实现Typescript函数重载?
interface Fn <T>{
(payload: string): T;
(payload: string, a: T): T;
}
let fn: Fn<number> = function(a: string, b: number) {
return b
}
报告Type '(a: string, b: number) => string' is not assignable to type 'Fn<number>'.Vetur(2322)
答案 0 :(得分:1)
为了使这两个重载都适用于fn
,必须在实现中将b
设为可选。这样做时,您会看到一个不同的错误,因为我们的类型表明必须返回一个number
,所以当b
可能为b
时返回undefined
不会剪了它。我们可以为b
设置一个默认值,或者在函数主体内返回一些默认值。
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;
}
答案 1 :(得分:1)
为了满足具有多个(即重载)调用签名的接口,函数的实现必须能够处理所有个签名。如果fn
是Fn<number>
,那么您必须能够这样调用它:
fn("someString"); // first overload
fn("someString", 1234); // second overload
但是实现
function (a: string, b: number) {
return b
}
不满足第一个重载(因为b
将是undefined
),因此不能分配给Fn<number>
。
相反,您应该考虑提出一种与两个呼叫签名都兼容的实现。例如:
let fn: Fn<number> = function (a: string, b?: number) {
return b ?? a.length
}
在这里,实现要求其第一个参数a
为string
,并采用类型为b
的可选第二个参数number
。由于b
可能是undefined
,而您必须返回number
,因此不能直接返回b
。在上面的代码中,我返回b
,除非它是undefined
,否则返回a.length
。始终是number
,编译器可以验证该分配是否有效。
注意:如果过载的呼叫签名具有不同的返回类型,则情况会很复杂。除非您的函数表达式的实现返回所有返回类型的intersection,否则编译器将无法验证类型的安全性并会出错,因此您将需要使用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
在这里您可以看到fn2
确实满足OtherFn
中的两个调用签名,但是由于返回类型不同,编译器无法对其进行验证。如果要使用函数表达式执行此操作,则需要类型断言:
let fn3 = ((a: string, b?: number) => b ?? a) as OtherFn; // okay
或者您可以放弃函数表达式,而使用函数语句,对它们进行更宽松的检查,使返回类型成为所有调用签名返回类型的union:
function fn4(payload: string): string;
function fn4(payload: string, a: number): number;
function fn4(a: string, b?: number) {
return b ?? a;
}
尽管没有被手动注释,仍可以满足OtherFn
。
let fn5: OtherFn = fn4; // okay