简体   繁体   English

包装重载的 Typescript 函数

[英]Wrapping an Overloaded Typescript Function

I'm having trouble writing a wrapper for an overloaded function.我在为重载函数编写包装器时遇到问题。

The function I'm wrapping is the SendGrid email sending function .我包装的功能是SendGrid 电子邮件发送功能 That function is overloaded like (simplified):该函数被重载(简化):

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

I want to write a function that can either accept an Email or Email[] .我想编写一个可以接受EmailEmail[]的函数。 But when I do it like this, it complains that no overload matches the call:但是当我这样做时,它抱怨没有过载匹配调用:

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

However, this works:但是,这有效:

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

I think that what's happening is that Email | Email[]我认为正在发生的事情是Email | Email[] Email | Email[] isn't a valid input for send(data: Email) or for send(data: Email[]) , and Typescript doesn't realize that the overloads mean that sendGrid.send(data) should be valid (and, indeed, if the if and else branches can have the same code and be valid, that makes it seem like Typescript should be able to realize that the statement is valid even without an if statement to help it infer a type since Typescript is compile-time only and won't actually direct the runtime code execution to make different function calls based on the types). Email | Email[]不是send(data: Email)send(data: Email[])的有效输入,Typescript 没有意识到重载意味着sendGrid.send(data)应该是有效的(并且,实际上,如果ifelse分支可以具有相同的代码并且是有效的,那么即使没有if语句来帮助它推断类型,Typescript 似乎也应该能够意识到该语句是有效的,因为 Typescript 是编译时只是并且实际上不会指导运行时代码执行根据类型进行不同的函数调用)。

My two questions:我的两个问题:

  1. What's the best practice in situations like these?在这种情况下,最佳做法是什么?
  2. Is this just a shortcoming in the Typescript compiler, or is there a good reason that sendEmailWrapper1 would fail to compile but sendEmailWrapper2 would work?这只是 Typescript 编译器的一个缺点,还是有充分的理由让sendEmailWrapper1无法编译但sendEmailWrapper2可以工作?

Thanks!谢谢!

This is a shortcoming of the the class MailService declaration.这是class MailService声明的一个缺点。 It could have a more accurate type definition like this:它可以有一个更准确的类型定义,如下所示:

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

The best practice, in my opinion, is to augment the type definition locally.在我看来,最佳实践是在本地增加类型定义。 Then submit a pull request to the repository with the change.然后向存储库提交一个包含更改的拉取请求。

Also, this is a wrapper that uses rest parameters.此外,这是一个使用 rest 参数的包装器。 It will work with less effort on your part:它将以更少的努力工作:

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

Actually this usage is not a good example of overloading, if you need to overload methods, it means you need to return different types when parameters' type is changed.实际上这种用法并不是重载的好例子,如果你需要重载方法,就意味着当参数类型改变时你需要返回不同的类型。 If you need different return types then overloading methods is more consistent.如果您需要不同的返回类型,则重载方法更加一致。 For example :例如 :

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

or if parameters have an optional property but when the property is set then an another property must be required.或者如果参数有一个可选的属性,但是当设置了该属性时,则必须需要另一个属性。

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

From the TypeScript documentation on Overloads :从关于Overloads的 TypeScript 文档:

JavaScript is inherently a very dynamic language. JavaScript 本质上是一种非常动态的语言。 It's not uncommon for a single JavaScript function to return different types of objects based on the shape of the arguments passed in.单个 JavaScript 函数根据传入参数的形状返回不同类型的对象的情况并不少见。

As Cenk's answer , the typing of send() should use overloads only if the return types are different.正如Cenk 的回答,只有当返回类型不同时, send()的类型才应该使用重载。 Otherwise, send(data: Email | Email[]): foo would be more appropriate and handy for users of the API.否则, send(data: Email | Email[]): foo对 API 用户来说更合适和方便。 So It's a good option for your wrapper to have this type.因此,您的包装器拥有这种类型是一个不错的选择。

To improve the wrapper body code in this case, we can convert data to Email[] to use only one overload and avoid the apparent code duplication:在这种情况下,为了改进包装器主体代码,我们可以将data转换为Email[]以仅使用一个重载并避免明显的代码重复:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM