gpt4 book ai didi

typescript - 重载签名、联合类型和 "No overload matches this call"错误

转载 作者:行者123 更新时间:2023-12-03 23:39:54 41 4
gpt4 key购买 nike

想象一个 TypeScript (4.2.2) 函数接收 stringPromise<string>并使用它最初收到的相同类型返回一个答案,例如:

function trim(textOrPromise) {
if (textOrPromise.then) {
return textOrPromise.then(value => result.trim());
}
return textOrPromise.trim();
}
我想使用泛型定义此类函数的签名,以指示传入 Promise总是导致 Promise ,并传入 string结果是 string为此,我定义了以下重载:
function trim(text: Promise<string>): Promise<string>
function trim(text: string): string
function trim(text: any): any {
if (text.then) {
return text.then(result => result.trim()); // returns Promise<string>
}
return text.trim(); // returns string
}
只要参数明确定义为 string 就可以正确转换或 Promise<string> :
let value: string
trim(value) // fine
let value: Promise<string>
trim(value) // fine
但是,当我使用 union type 定义参数时( Promise<string> | string ):
let value: Promise<string> | string
trim(value) // error: TS2769
我收到以下转译错误:
TS2769: No overload matches this call.
  Overload 1 of 2, '(text: Promise<string>): Promise<string>', gave the following error.
    Argument of type 'string | Promise<string>' is not assignable to parameter of type 'Promise<string>'.
      Type 'string' is not assignable to type 'Promise<string>'.
  Overload 2 of 2, '(text: string): string', gave the following error.
    Argument of type 'string | Promise<string>' is not assignable to parameter of type 'string'.
      Type 'Promise<string>' is not assignable to type 'string'.
有趣的是,当我将联合类型添加到函数签名时,该示例会正确地进行转换和行为
function trim(text: Promise<string>): Promise<string>
function trim(text: string): string
function trim(text: Promise<string> | string): Promise<string> | string
function trim(text: any): any {
if (text.then) {
return text.then(result => result.trim());
}
return text.trim();
}

let value: Promise<string> | string
trim(value) // fine
使用最后一个实现,TypeScript 知道当函数收到 Promise 时它将返回 Promise ,当它收到 string它返回一个 string .与 Promise<string> | string 的联合相反正如第三次重载所暗示的那样。
如果有人可以解释这种行为以及必须为联合类型添加重载的原因,我将不胜感激。

最佳答案

将联合传递给过载
在根据您的重载签名检查联合之前,Typescript 无法“拆分”联合。它正在检查您的 Promise<string> | string 类型的变量单独对抗每个过载。联合不能分配给它的任何一个成员,所以没有接受 Promise<string> | string 的重载签名。 .
这是一个已知的行为,并且可以追溯到几年前有关此的一些 GitHub 问题。

  • Support overload resolution with type union arguments #14107
  • Union types not working with old-style overloads #1805

  • 反对改变 typescript 行为的论点似乎是,当您有一个具有多个参数且每个参数都接受多种类型的函数时,可能的组合数量可能会迅速增加。
    由于 typescript 本身不支持这一点,因此您必须手动添加一个接受联合并返回联合的重载,就像您已经完成的那样。请注意,实现签名(重载的最后一行)不算作重载之一。因此,仅在实现签名中使用联合是不够的。
    function trim(text: Promise<string>): Promise<string>;
    function trim(text: string): string;
    function trim(text: Promise<string> | string): Promise<string> | string;
    function trim(text: Promise<string> | string): Promise<string> | string {
    if (typeof text === "string") {
    return text.trim();
    } else {
    return text.then(result => result.trim());
    }
    }
    泛型
    我们在使用泛型而不是重载时没有这个问题,因为 extends包括工会。 T extends A | B意味着 T可以 A , B ,或工会 A | B (或这些的任何更精致的版本,我稍后会介绍)。
    function trim<T extends Promise<string> | string>(text: T): T {
    然而,泛型在函数的实现方面会提出自己的问题,因为改进了变量 text 的类型。不细化泛型 T 的类型.考虑到类型 T,这种行为通常是有意义的。可以是工会。
    在这里令人沮丧,特别是因为我们返回与输入相同的类型。所以如果我们知道 textstring那么我们知道 T必须包括 string所以返回 string 应该没问题, 对?不。
    感觉我们只有三种可能性( stringPromise<string>Promise<string> | string )。但是 extendsT 打开无限可能的类型那是那些的改进。如 T是文字串 " A "然后是 string "A"我们从 text.trim() 返回真的不能分配给 T .所以我们解决了一个问题,但又创造了另一个问题。我们必须做 as断言以避免错误,例如:

    Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | Promise'.(2322)


    还有一些奇怪的边缘情况,这些断言可能是不正确的。
    function trim<T extends Promise<string> | string>(text: T): T {
    if (text instanceof Promise) {
    return text.then((result) => result.trim()) as T;
    }
    return (text as string).trim() as T;
    }

    const a = trim(' ' as Promise<string> | string); // type: string | Promise<string>
    const b = trim(' '); // type: ' ' -- actually wrong!
    const c = trim(' ' as string); // type: string
    const d = trim(new Promise<string>(() => ' ')); // type: Promise<string>
    const e = trim(new Promise<' '>(() => ' ')); // type: Promise<' '> -- wrong again!
    Typescript Playground Link
    所以我更喜欢重载版本,即使你需要额外的行。

    关于typescript - 重载签名、联合类型和 "No overload matches this call"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66507585/

    41 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com