【问题标题】:Wrapping an Overloaded Typescript Function包装重载的 Typescript 函数
【发布时间】:2020-06-08 13:28:45
【问题描述】:

我在为重载函数编写包装器时遇到问题。

我包装的函数是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 没有意识到重载意味着 sendGrid.send(data) 应该是有效的(而且,确实,如果ifelse 分支可以具有相同的代码并且是有效的,那么看起来Typescript 应该能够意识到即使没有if 语句来帮助它推断类型,该语句也是有效的Typescript 只是编译时的,实际上不会指导运行时代码执行以根据类型进行不同的函数调用)。

我的两个问题:

  1. 在这种情况下,最佳做法是什么?
  2. 这只是 Typescript 编译器的一个缺点,还是有充分的理由 sendEmailWrapper1 无法编译但 sendEmailWrapper2 可以工作?

谢谢!

【问题讨论】:

  • MailService 的类型定义在哪里? IE。它来自什么npm install xxx
  • 我在 GitHub 中链接了该文件,该文件在我的问题描述顶部进行了定义——它是 SendGrid Node v3 邮件库。 @sendgrid/邮件。

标签: typescript


【解决方案1】:

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

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

在我看来,最佳实践是在本地增加类型定义。然后将拉取请求提交到包含更改的存储库。

另外,这是一个使用剩余参数的包装器。对您而言,它会更轻松地工作:

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

【讨论】:

    【解决方案2】:

    其实这种用法并不是一个很好的重载例子,如果你需要重载方法,那就意味着当参数的类型改变时,你需要返回不同的类型。如果您需要不同的返回类型,则重载方法更加一致。 例如:

    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 函数根据传入参数的形状返回不同类型的对象并不少见。

    【讨论】:

    • “如果你需要重载方法,这意味着你需要在参数类型改变时返回不同的类型” 并非总是如此。一个例子是,如果您想要一个具有两个可选参数的函数,但如果您提供一个,那么您也必须提供另一个。在这种情况下,你可以有一个没有参数的重载,另一个有两个非可选参数的重载。
    • 我不明白这个答案。你是说我的代码应该做一些不同的事情还是 SendGrid 的代码应该做一些不同的事情?
    【解决方案3】:

    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)

    【讨论】:

      猜你喜欢
      • 2022-11-04
      • 1970-01-01
      • 2012-10-24
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多