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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42427393/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:08:34  来源:igfitidea点击:

Cannot invoke an expression whose type lacks a call signature

javascripttypescripttypes

提问by Erdem

I have apple and pears - both have an isDecayedattribute:

我有苹果和梨——两者都有一个isDecayed属性:

interface Apple {
    color: string;
    isDecayed: boolean;
}

interface Pear {
    weight: number;
    isDecayed: boolean;
}

And both types can be in my fruit basket (multiple times):

这两种类型都可以在我的水果篮中(多次):

interface FruitBasket {
   apples: Apple[];
   pears: Pear[];
}

Let's assume for now my basket is empty:

现在让我们假设我的篮子是空的:

const fruitBasket: FruitBasket = { apples: [], pears: [] };

Now we take randomly one kind out of the basket:

现在我们从篮子里随机取出一种:

const key: keyof FruitBasket = Math.random() > 0.5 ? 'apples': 'pears'; 
const fruits = fruitBasket[key];

And of course nobody likes decayed fruits so we pick only the fresh ones:

当然,没有人喜欢腐烂的水果,所以我们只挑选新鲜的:

const freshFruits = fruits.filter((fruit) => !fruit.isDecayed);

Unfortunately Typescript tells me:

不幸的是,Typescript 告诉我:

Cannot invoke an expression whose type lacks a call signature. Type '((callbackfn: (value: Apple, index: number, array: Apple[]) => any, thisArg?: any) => Apple[]) | ...' has no compatible call signatures.

无法调用类型缺少调用签名的表达式。输入'((callbackfn: (value: Apple, index: number, array: Apple[]) => any, thisArg?: any) => Apple[]) | ...' 没有兼容的调用签名。

What's wrong here - is it just that Typescript doesn't like fresh fruits or is this a Typescript bug?

这里有什么问题 - 只是 Typescript 不喜欢新鲜水果还是这是 Typescript 的错误?

You can try it yourself in the official Typescript Repl.

您可以在官方Typescript Repl 中自己尝试。

回答by Sefe

TypeScript supports structural typing (also called duck typing), meaning that types are compatible when they share the same members. Your problem is that Appleand Peardon't share all their members, which means that they are not compatible. They are however compatible to another type that has only the isDecayed: booleanmember. Because of structural typing, you don' need to inherit Appleand Pearfrom such an interface.

TypeScript 支持结构类型(也称为鸭子类型),这意味着类型在共享相同成员时是兼容的。你的问题是,ApplePear没有分享自己的所有成员,这意味着它们是不兼容的。然而,它们与只有isDecayed: boolean成员的另一种类型兼容。由于结构类型,您不需要从这样的接口继承ApplePear

There are different ways to assign such a compatible type:

有多种方法可以分配这种兼容类型:

Assign type during variable declaration

在变量声明期间分配类型

This statement is implicitly typed to Apple[] | Pear[]:

该语句被隐式输入为Apple[] | Pear[]

const fruits = fruitBasket[key];

You can simply use a compatible type explicitly in in your variable declaration:

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

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

For additional reusability, you can also define the type first and then use it in your declaration (note that the Appleand Pearinterfaces don't need to be changed):

为了获得额外的可重用性,您还可以先定义类型,然后在声明中使用它(请注意,不需要更改ApplePear接口):

type Fruit = { isDecayed: boolean };
const fruits: Fruit[] = fruitBasket[key];

Cast to compatible type for the operation

转换为操作的兼容类型

The problem with the given solution is that it changes the type of the fruitsvariable. This might not be what you want. To avoid this, you can narrow the array down to a compatible type before the operation and then set the type back to the same type as fruits:

给定解决方案的问题在于它改变了fruits变量的类型。这可能不是您想要的。为避免这种情况,您可以在操作之前将数组缩小到兼容的类型,然后将类型设置回与 相同的类型fruits

const fruits: fruitBasket[key];
const freshFruits = (fruits as { isDecayed: boolean }[]).filter(fruit => !fruit.isDecayed) as typeof fruits;

Or with the reusable Fruittype:

或者使用可重用Fruit类型:

type Fruit = { isDecayed: boolean };
const fruits: fruitBasket[key];
const freshFruits = (fruits as Fruit[]).filter(fruit => !fruit.isDecayed) as typeof fruits;

The advantage of this solution is that both, fruitsand freshFruitswill be of type Apple[] | Pear[].

此解决方案的优点是,fruitsfreshFruits都属于Apple[] | Pear[]

回答by shusson

As mentioned in the github issueoriginally linked by @peter in the comments:

正如@peter 在评论中最初链接的github 问题中所述:

const freshFruits = (fruits as (Apple | Pear)[]).filter((fruit: (Apple | Pear)) => !fruit.isDecayed);

回答by Frambot

Perhaps create a shared Fruitinterface that provides isDecayed. fruitsis now of type Fruit[]so the type can be explicit. Like this:

也许创建一个Fruit提供 isDecayed的共享接口。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);

回答by thewindev

I had the same issue with numeral, a JS library. The fix was to install the typings again with this command:

我对数字有同样的问题,一个 JS 库。修复方法是使用以下命令再次安装类型:

npm install --save @types/numeral