【发布时间】:2021-06-05 00:31:31
【问题描述】:
想象一个 TypeScript (4.2.2) 函数接收 string 或 Promise<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的重载签名。但是为什么它不能在检查之前拆分工会呢?我不知道。 -
非常感谢琳达。我可以使用第三个重载或条件类型来解决它,但我想了解这种行为背后的机制。
-
@Jan 我发现了关于它的 GitHub 问题:github.com/microsoft/TypeScript/issues/1805 和 github.com/microsoft/TypeScript/issues/14107 那里有很多阅读。
标签: typescript typescript-generics