首页 > 解决方案 > 为什么 TypeScript 声称它“无法调用类型缺少调用签名的表达式”?

问题描述

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

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

据我了解,如果我有这样的类型:

type Something = number[] | string[]

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

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)[]应该表现相同。

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

标签: typescript

解决方案


实际上,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'.

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

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

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


推荐阅读