接口的重载方法不能接受联合类型:“没有重载匹配此调用”

Posted

技术标签:

【中文标题】接口的重载方法不能接受联合类型:“没有重载匹配此调用”【英文标题】:Overloaded method of an interface cannot accept a union type: "No overload matches this call" 【发布时间】:2021-05-26 16:54:11 【问题描述】:

我使用了一个第 3 方库,它定义了一个接口 (A) 和一个重载方法 (method)。该方法可以接受空值或字符串值作为参数。

我定义了一个联合类型:string | null,并将它作为参数传递给方法,但我得到一个错误:

No overload matches this call.
  Overload 1 of 2, '(val: null): null', gave the following error.
    Argument of type 'StringOrNull' is not assignable to parameter of type 'null'.
      Type 'string' is not assignable to type 'null'.
  Overload 2 of 2, '(val: string): string', gave the following error.
    Argument of type 'StringOrNull' is not assignable to parameter of type 'string'.
      Type 'null' is not assignable to type 'string'.(2769)

这是我的代码: TypeScript Playgroud

interface A 
  method(val: null): null;
  method(val: string): string;

type StringOrNull = string | null;

function myFunc(obj: A, val: StringOrNull) 
  obj.method(val); // Error: No overload matches this call.

为什么会发生这种情况,我该如何解决?

【问题讨论】:

我在你的例子中有三个错误。你能解决它吗? @captain-yossarian 谢谢你的评论。我编辑了我的示例以仅包含相关错误。 【参考方案1】:

嗯,这是因为您提供的所有重载签名都不接受 string | null 作为 val 参数类型。从编译器的角度考虑这一点,在您的 A 接口中,您保证:

    method 可以接受null 并返回null method 可以接受string 并返回string

然后,您传入了 string | null 的联合 - 正如预期的那样,编译器抱怨没有 (val: string | null) => unknown(或类似的)签名(“没有重载匹配此调用”)。

错误消息告诉你编译器试图做什么:

    检查(val: string) => string签名时将string | null分配给string 检查(val: null) => null签名时将string | null分配给null

如您所见,val 的两种类型都与StringOrNull 联合不兼容。我认为在这种情况下(坦率地说,总的来说)写一个受约束的泛型比函数重载更好(此外,你的方法是identity function):

interface A 
  method<T extends StringOrNull>(val: T) : T;


type StringOrNull = string | null;

function myFunc(obj: A, val: StringOrNull) 
  obj.method(val); //OK

Playground


顺便说一句:手册 2.0 在article about overloads 中专门解决了这个陷阱:

TypeScript 只能将函数调用解析为单个重载

来自 cmets 讨论的第二条注释:如果您不控制 A 接口的声明,您可以利用 declaration merging 技术来提供您自己的签名:

interface A 
  method(val: string) : string;


interface A 
  method<T extends StringOrNull>(val: T) : T;


type StringOrNull = string | null;

function myFunc(obj: A, val: StringOrNull) 
  obj.method(val); //OK

如果接口是从模块中导入的,则必须使用上述与module augmentation配对。

【讨论】:

感谢您的回答。你有我的赞成票。不幸的是,我无法控制interface A。它来自我使用的一个库(我在我的问题中指出了它)。您对如何更改myFunc 注释有什么建议吗? @SergeyGoliney - 好吧(顺便说一句,你可能应该提到你不能更改接口本身) - 你可以利用接口合并并使用该方法的通用版本再次声明 A - 他们应该合并,你会得到你需要的 - 我很快就会添加一个到操场的链接 我看到的另一种方法是扩展接口,如果你不喜欢合并,但我认为如果你不控制接口的定义方式,你就无能为力了 如果库是一个模块,上面提到的可以用module augmentation补充以使事情正常工作(除非A是默认导出-在这种情况下你搞砸了,必须修补界面随时使用) @SergeyGoliney - NP。向 3P lib 作者提出这个问题可能也是一个好主意 - 这是编写重载的一种糟糕方式,因此他们绝对应该对类型进行更新。

以上是关于接口的重载方法不能接受联合类型:“没有重载匹配此调用”的主要内容,如果未能解决你的问题,请参考以下文章

重载与重写

在 TypeScript 中重载联合类型

Java重载覆写thissuper抽象类接口

如何定义“类型析取”(联合类型)?

TypeScript 中的函数重载与使用联合类型

JAVA --- 重载和重写的区别