如何实现打字稿功能重载

时间:2020-11-05 01:11:54

标签: typescript

如何实现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)

2 个答案:

答案 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)

为了满足具有多个(即重载)调用签名的接口,函数的实现必须能够处理所有个签名。如果fnFn<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
}

在这里,实现要求其第一个参数astring,并采用类型为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

Playground link to code