包装重载的打字稿功能

时间:2020-02-24 21:15:52

标签: typescript

我无法为重载函数编写包装器。

我要包装的函数是the SendGrid email sending function。该函数如(简化)那样被重载:

declare class MailService {
  send(data: Email): foo;
  send(data: Email[]): foo;
}

我想编写一个可以接受EmailEmail[]的函数。但是当我这样做时,它抱怨没有重载匹配该调用:

function sendEmailWrapper1(data: Email | Email[]) {
  sendGrid.send(data)
}

但是,这可行:

function sendEmailWrapper2(data: Email | Email[]) {
  if ('length' in data) {
    sendGrid.send(data)
  } else {
    sendGrid.send(data)
  }
}

我认为正在发生的事情是Email | Email[]不是send(data: Email)send(data: Email[])的有效输入,而Typescript并未意识到过载意味着{{1} }应该是有效的(并且实际上,如果sendGrid.send(data)if分支可以具有相同的代码并且是有效的,这使Typescript似乎应该能够意识到该语句即使没有else语句可帮助推断类型,因为Typescript仅在编译时,实际上不会指导运行时代码执行根据类型进行不同的函数调用。

我的两个问题:

  1. 在这种情况下的最佳做法是什么?
  2. 这只是Typescript编译器的不足之处吗?还是有充分的理由if无法编译而sendEmailWrapper1可以工作?

谢谢!

3 个答案:

答案 0 :(得分:2)

这是class MailService声明的缺点。它可能具有更准确的类型定义,如下所示:

declare class MailService {
  send(data: Email | Email[]): foo;
}

我认为,最佳实践是在本地扩展类型定义。然后将更改请求提交到存储库。

此外,这是一个使用rest参数的包装器。这样您就可以轻松完成工作:

// Accepts on or more MailData objects.
// sendEmailWrapper3(data);
// sendEmailWrapper3(data, data1);
function sendEmailWrapper3(...data: MailData[]) {
  sendGrid.send(data);
}

答案 1 :(得分:1)

实际上,这种用法不是重载的一个很好的例子,如果需要重载方法,则意味着在更改参数的类型时需要返回不同的类型。如果需要不同的返回类型,则重载方法更加一致。 例如:

send(data: Email): foo; 
send(data: Email[]): foo[]

或者如果参数具有可选属性,但是设置了属性后,则必须要求另一个属性。

function bar(params:{
    prop1: number;
    prop2?: boolean;
}): Foo;
function bar(params:{
    prop1: number;
    prop2: boolean;
    prop3: string;
}): Foo;

摘自Overloads上的TypeScript文档:

JavaScript本质上是一种非常动态的语言。单个JavaScript函数根据传入参数的形状返回不同类型的对象的情况并不少见。

答案 2 :(得分:0)

Cenk's answer一样,send()的类型仅在返回类型不同时才应使用重载。否则,send(data: Email | Email[]): foo对于API用户将更合适和方便。因此,对于您的包装器来说,具有这种类型是一个不错的选择。

在这种情况下,为了改进包装器主体代码,我们可以将data转换为Email[]以仅使用一个重载并避免明显的代码重复:

  • const emails = Array.isArray(data) ? data : [data]
  • const emails = ([] as Email[]).concat(data)然后
  • sendGrid.send(emails)