无法调用其类型缺少调用签名的表达式

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 显式设置为stringconst 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。您的问题是 ApplePear 不共享所有成员,这意味着它们不兼容。但是,它们与仅具有 isDecayed: boolean 成员的另一种类型兼容。由于结构类型,您不需要从这样的接口继承ApplePear

分配这种兼容类型有不同的方法:

在变量声明期间分配类型

此语句隐式键入Apple[] | Pear[]

const fruits = fruitBasket[key];

您可以简单地在变量声明中显式使用兼容类型:

const fruits:  isDecayed: boolean [] = fruitBasket[key];

为了增加可重用性,您也可以先定义类型,然后在声明中使用它(注意ApplePear 接口不需要更改):

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;

此解决方案的优点是fruitsfreshFruits 的类型均为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 也可能只是 Fruits 的数组

以上是关于无法调用其类型缺少调用签名的表达式的主要内容,如果未能解决你的问题,请参考以下文章

无法调用其类型缺少调用签名的表达式

类型数组的打字稿错误:- 无法调用其类型缺少调用签名的表达式

错误 TS2349:无法调用其类型缺少调用签名的表达式

使用 node-fetch 时无法调用其类型缺少调用签名的表达式

Typescript中的“无法调用类型缺少调用签名的表达式”?

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