为啥 TypeScript 声称它“无法调用类型缺少调用签名的表达式”?

Posted

技术标签:

【中文标题】为啥 TypeScript 声称它“无法调用类型缺少调用签名的表达式”?【英文标题】:Why does TypeScript claim it "Cannot invoke an expression whose type lacks a call signature"?为什么 TypeScript 声称它“无法调用类型缺少调用签名的表达式”? 【发布时间】:2019-06-19 19:01:35 【问题描述】:

编辑:根据要求,TypeScript 版本为 3.2.2

关于 *** 上的这个特定错误有很多问题和答案,但没有一个能令人满意地解释为什么会发生这种情况。

在我的理解中,如果我有这样的类型:

type Something = number[] | string[]

那么Something 可以是数字数组或任何字符串数组。无论数组的内容如何,​​它都应该具有filtermap 之类的属性。但是如果我在函数中使用这种类型:

function doSomething(s: Something): void 
  s.map()

然后抛出cannot invoke an expression....错误。

这很容易通过将Something 更改为:

type Something = (number | string)[]

现在 TypeScript 不会抱怨调用签名,但我不明白为什么。出于所有意图和目的,(number | string)[]number[] | string[] 似乎与我相同。它可以是一个包含数字的数组,也可以是一个包含字符串的数组。在定义这样的类型时,调用签名似乎甚至不相关。

文档甚至说:

联合类型描述的值可以是多种类型之一。我们使用竖线 (|) 来分隔每种类型,所以 number |字符串 | boolean 是值的类型,可以是数字、字符串或布尔值。

还有:

如果一个值的类型为 A | B,我们只知道它有 A 和 B 都有的成员。

因此,即使从我在官方文档中看到的内容来看,number[] | string[](number | string)[] 的行为也应该相同。

这是一个错误吗?我错过了重点还是只是过于密集?我知道这对大多数人来说似乎无关紧要,但我真的很想理解这一点,所以我希望有人能提供一个体面的解释。

【问题讨论】:

This 似乎相关 【参考方案1】:

实际上,TypeScript 团队just wrote about improvements to calling methods on union objects 几天前发布了 TypeScript 3.3。 main PR implementing the improvement 也有有用的上下文。 TypeScript 团队还有written about why it's a tricky problem to solve in a recent language design meeting。


简而言之,当第一次设计联合类型的调用方法时,TypeScript 团队最初“为了安全起见,大部分时间都错误地认为签名必须相同”才能使方法可调用。

TypeScript 3.3 的最新变化在一定程度上改善了这种情况,当参数共享一个公共类型时,联合上的方法调用现在可以工作,例如:

type Fruit = "apple" | "orange";
type Color = "red" | "orange";

type FruitEater = (fruit: Fruit) => number;     // eats and ranks the fruit
type ColorConsumer = (color: Color) => string;  // consumes and describes the colors

declare let f: FruitEater | ColorConsumer;

f("orange"); // It works! Returns a 'number | string'.

然而,即使有了这个新的修复:

仅当联合中最多一种类型具有多个重载,并且联合中最多一种类型具有通用签名时,此新行为才会生效。这意味着 number[] | string[] 上的方法,如 map(这是通用的)仍然无法调用。

本质上,TypeScript 团队要合理实现这只是一个相当复杂的问题,他们还没有完全制定出最好的解决方案。公平地说,虽然当前的行为不是应该工作的方式,所以如果你愿意,你可以称之为错误。

【讨论】:

这很有启发性,非常感谢您分享它。在 PR 中,他提到“有了更多的基础设施,这可能是可以修复的”,我想知道你是否知道他的意思?不管怎样,这回答了我的问题,然后一些,我想我现在会忽略这种行为 我认为他的意思是,TypeScript 代码库中用于处理这种性质的类型的现有代码尚未开发到足以轻松处理相关问题的程度。我不知道具体缺少什么。

以上是关于为啥 TypeScript 声称它“无法调用类型缺少调用签名的表达式”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 TypeScript 接受被覆盖方法的正确返回,即使它不可访问?

为啥我不能访问 TypeScript 私有成员?

为啥 TypeScript 中引入了 `never` 类型?

为啥 Typescript 不以正确的方式支持函数重载?

为啥我的 Typescript 对象允许这个额外的属性?

为啥 Typescript 编译器不会将 .ts 更改为 .js?