web-dev-qa-db-fra.com

Impossible d'appeler une expression dont le type ne contient pas de signature d'appel

J'ai Apple et des poires - les deux ont un attribut isDecayed:

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

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

Et les deux types peuvent être dans mon panier de fruits (plusieurs fois):

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

Supposons pour l'instant que mon panier est vide:

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

Maintenant, nous prenons au hasard un type du panier:

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

Et bien sûr, personne n'aime les fruits pourris, alors nous ne cueillons que les fruits frais:

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

Malheureusement, TypeScript me dit:

Impossible d'appeler une expression dont le type ne contient pas de signature d'appel. Tapez '((callbackfn: (valeur: Apple, index: numéro, tableau: Apple []) => any, thisArg ?: any) => Apple []) | ... 'n'a pas de signatures d'appel compatibles.

Quel est le problème ici - est-ce juste que TypeScript n'aime pas les fruits frais ou est-ce un bogue TypeScript?

Vous pouvez l'essayer vous-même dans le fichier officiel TypeScript Repl .

63
Erdem

TypeScript prend en charge le typage structural (également appelé typage de canard), ce qui signifie que les types sont compatibles lorsqu'ils partagent les mêmes membres . Votre problème est que Apple et Pear ne partagent pas tous leurs membres, ce qui signifie qu'ils ne sont pas compatibles. Ils sont cependant compatibles avec un autre type ne comportant que le membre isDecayed: boolean. En raison du typage structurel, vous n'avez pas besoin d'hériter de Apple et Pear d'une telle interface.

Il existe différentes manières d’attribuer un tel type compatible:

Affecter le type lors de la déclaration de variable

Cette instruction est implicitement saisie dans Apple[] | Pear[]:

const fruits = fruitBasket[key];

Vous pouvez simplement utiliser explicitement un type compatible dans votre déclaration de variable:

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

Pour une réutilisation supplémentaire, vous pouvez également définir le type en premier lieu, puis l'utiliser dans votre déclaration (notez que les interfaces Apple et Pear n'ont pas besoin d'être modifiées):

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

Distribution dans un type compatible pour l'opération

Le problème avec la solution donnée est que cela change le type de la variable fruits. Ce n'est peut-être pas ce que vous voulez. Pour éviter cela, vous pouvez réduire le tableau à un type compatible avant l'opération, puis redéfinir le type sur le même type que fruits:

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

Ou avec le type réutilisable Fruit:

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

L'avantage de cette solution est que fruits et freshFruits seront tous deux de type Apple[] | Pear[].

72
Sefe

Peut-être créer une interface partagée Fruit qui fournit isDecayed. fruits est maintenant de type Fruit[] afin que le type puisse être explicite. Comme ça:

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);
4
Frambot

Comme mentionné dans le numéro de github lié initialement par @peter dans les commentaires:

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