无法调用其类型缺少调用签名的表达式
Posted
技术标签:
【中文标题】无法调用其类型缺少调用签名的表达式【英文标题】:Cannot invoke an expression whose type lacks a call signature 【发布时间】:2017-07-14 14:55:12 【问题描述】:我有苹果和梨 - 两者都有 isDecayed
属性:
interface Apple
color: string;
isDecayed: boolean;
interface Pear
weight: number;
isDecayed: boolean;
这两种类型都可以在我的水果篮中(多次):
interface FruitBasket
apples: Apple[];
pears: Pear[];
现在假设我的购物篮是空的:
const fruitBasket: FruitBasket = apples: [], pears: [] ;
现在我们从篮子里随机取出一种:
const key: keyof FruitBasket = Math.random() > 0.5 ? 'apples': 'pears';
const fruits = fruitBasket[key];
当然没有人喜欢腐烂的水果,所以我们只挑选新鲜的:
const freshFruits = fruits.filter((fruit) => !fruit.isDecayed);
不幸的是 Typescript 告诉我:
无法调用类型缺少调用签名的表达式。输入 '((callbackfn: (value: Apple, index: number, array: Apple[]) => any, thisArg?: any) => Apple[]) | ...' 没有兼容的调用签名。
这里出了什么问题 - 仅仅是 Typescript 不喜欢新鲜水果还是这是一个 Typescript 错误?
你可以在官方Typescript Repl自己试试。
【问题讨论】:
试过googlegithub.com/Microsoft/TypeScript/issues/7960 有什么理由不能用isDecayed
属性扩展一个通用的Fruit
接口,然后将fruits 声明为Fruit[]
类型?
奇怪...如果您将key
显式设置为string
即const key: string = Math.random() > 0.5 ? 'apples': 'pears';
没有错误
^ 发生是因为没有明确的索引签名,返回类型将是 any
这可以绕过问题。
【参考方案1】:
我在 JS 库中遇到了同样的问题。解决方法是使用以下命令再次安装类型:
npm install --save @types/numeral
【讨论】:
【参考方案2】:TypeScript 支持结构类型(也称为鸭子类型),这意味着 types are compatible when they share the same members。您的问题是 Apple
和 Pear
不共享所有成员,这意味着它们不兼容。但是,它们与仅具有 isDecayed: boolean
成员的另一种类型兼容。由于结构类型,您不需要从这样的接口继承Apple
和Pear
。
分配这种兼容类型有不同的方法:
在变量声明期间分配类型
此语句隐式键入Apple[] | Pear[]
:
const fruits = fruitBasket[key];
您可以简单地在变量声明中显式使用兼容类型:
const fruits: isDecayed: boolean [] = fruitBasket[key];
为了增加可重用性,您也可以先定义类型,然后在声明中使用它(注意Apple
和Pear
接口不需要更改):
type Fruit = isDecayed: boolean ;
const fruits: Fruit[] = fruitBasket[key];
转换为操作的兼容类型
给定解决方案的问题在于它改变了fruits
变量的类型。这可能不是你想要的。为避免这种情况,您可以在操作之前将数组缩小为兼容类型,然后将类型设置回与 fruits
相同的类型:
const fruits: fruitBasket[key];
const freshFruits = (fruits as isDecayed: boolean []).filter(fruit => !fruit.isDecayed) as typeof fruits;
或者使用可重复使用的Fruit
类型:
type Fruit = isDecayed: boolean ;
const fruits: fruitBasket[key];
const freshFruits = (fruits as Fruit[]).filter(fruit => !fruit.isDecayed) as typeof fruits;
此解决方案的优点是fruits
和freshFruits
的类型均为Apple[] | Pear[]
。
【讨论】:
【参考方案3】:正如 @peter 在 cmets 中最初链接的 github issue 中所述:
const freshFruits = (fruits as (Apple | Pear)[]).filter((fruit: (Apple | Pear)) => !fruit.isDecayed);
【讨论】:
我没有苹果和梨的混合数组,而是苹果数组或梨数组。 我使用 Typescript 的相同原因:为了构建我的代码,我或第三方可以更好地了解代码在做什么以及代码正在处理什么。苹果和梨的混合阵列不会出现在那个阵列中。那我为什么要把它定义为混合数组呢? 如果我错了,请纠正我,但数组将包含 Apples 或 Pears 的声明不是错误的,它只是没有描述全部事实。 你完全正确。但是,如果我有机会更详细地描述它,我想使用该方法,这就是我不明白 Typescript 将更详细的方法作为错误处理的原因。【参考方案4】:也许创建一个提供 isDecayed 的共享Fruit
接口。 fruits
现在是 Fruit[]
类型,因此类型可以是显式的。像这样:
interface Fruit
isDecayed: boolean;
interface Apple extends Fruit
color: string;
interface Pear extends Fruit
weight: number;
interface FruitBasket
apples: Apple[];
pears: Pear[];
const fruitBasket: FruitBasket = apples: [], pears: [] ;
const key: keyof FruitBasket = Math.random() > 0.5 ? 'apples': 'pears';
const fruits: Fruit[] = fruitBasket[key];
const freshFruits = fruits.filter((fruit) => !fruit.isDecayed);
【讨论】:
谢谢 - 这是我在此期间使用的解决方法,但对我来说,它仍然更像是一种 hack,而不是真正的解决方案。这是唯一的方法吗? 这种情况经常发生。您的FruitBasket
也可能只是 Fruit
s 的数组以上是关于无法调用其类型缺少调用签名的表达式的主要内容,如果未能解决你的问题,请参考以下文章
类型数组的打字稿错误:- 无法调用其类型缺少调用签名的表达式
使用 node-fetch 时无法调用其类型缺少调用签名的表达式