我无法为重载函数编写包装器。
我要包装的函数是the SendGrid email sending function。该函数如(简化)那样被重载:
declare class MailService {
send(data: Email): foo;
send(data: Email[]): foo;
}
我想编写一个可以接受Email
或Email[]
的函数。但是当我这样做时,它抱怨没有重载匹配该调用:
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仅在编译时,实际上不会指导运行时代码执行根据类型进行不同的函数调用。
我的两个问题:
if
无法编译而sendEmailWrapper1
可以工作?谢谢!
答案 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)