为什么TypeScript声称它"Cannot invoke an expression whose type lacks a call signature"?



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

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

根据我的理解,如果我有一种类型,比如:

type Something = number[] | string[]

那么CCD_ 1可以是数字数组,也可以是任何字符串数组。不管数组的内容如何,它都应该具有类似filtermap的属性。但如果我在函数中使用这种类型:

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

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

通过将Something更改为:可以很容易地解决此问题

type Something = (number | string)[]

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

文件甚至说:

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

和:

如果一个值的类型为a|B,我们只能确定它有a和B都有的成员。

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

这是个虫子吗?我是没有抓住要点,还是只是过于密集?我知道这对大多数人来说似乎无关紧要,但我真的在努力理解这一点,所以我希望有人能提供一个像样的解释。

事实上,TypeScript团队几天前刚刚写了关于在联合对象上调用方法的改进,发布了TypeScript 3.3。实施改进的主要PR也有有益的背景。TypeScript团队还在最近的一次语言设计会议上写下了为什么这是一个需要解决的棘手问题。


简而言之,当第一次在联合类型上设计调用方法时,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'.

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

只有当联合中最多一个类型有多个重载,并且联合中最多有一个类型具有泛型签名时,这种新行为才会生效。这意味着Something0上的方法,如map(它是泛型的),仍然是不可调用的。

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

相关内容

最新更新