78 votes

Impossible d'invoquer une expression dont le type ne possède pas de signature d'appel.

J'ai des pommes et des poires - les deux ont une isDecayed attribut :

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

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

Et les deux types peuvent se retrouver dans mon panier de fruits (plusieurs fois) :

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

Supposons pour l'instant que mon panier soit vide :

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

Maintenant, nous prenons au hasard une sorte dans le 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, donc nous ne prenons que les frais :

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

Malheureusement, Typescript me dit :

Impossible d'invoquer une expression dont le type ne comporte pas de signature d'appel. Type '((callbackfn : (valeur : Pomme, index : nombre, tableau : Pomme[]) => tout, thisArg? : tout) => Pomme[]) | ...' n'a pas de signature d'appel compatible.

Qu'est-ce qui ne va pas ici - est-ce simplement que Typescript n'aime pas les fruits frais ou est-ce un bug de Typescript ?

Vous pouvez l'essayer vous-même dans le site officiel [Typescript Repl](https://www.typescriptlang.org/play/#src=interface%20Apple%20%7B%0D%0A%20%20%20%20color%3A%20string%3B%0D%0A%20%20%20%20isDecayed%3A%20boolean%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20Pear%20%7B%0D%0A%20%20%20%20weight%3A%20number%3B%0D%0A%20%20%20%20isDecayed%3A%20boolean%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20FruitBasket%20%7B%0D%0A%20%20%20%20apples%3A%20Apple%5B%5D%3B%0D%0A%20%20%20%20pears%3A%20Pear%5B%5D%3B%0D%0A%7D%0D%0A%0D%0A%0D%0Aconst%20fruitBasket%3A%20FruitBasket%20%3D%20%7B%20apples%3A%20%5B%5D%2C%20pears%3A%20%5B%5D%20%7D%3B%0D%0Aconst%20key%3A%20keyof%20FruitBasket%20%3D%20Math.random()%20%3E%200.5%20%3F%20'apples'%3A%20'pears'%3B%20%0D%0Aconst%20fruits%20%3D%20fruitBasket%5Bkey%5D%3B%0D%0A%0D%0Aconst%20freshFruits%20%3D%20fruits.filter((fruit)%20%3D%3E%20!fruit.isDecayed)%3B) .

0 votes

Après avoir essayé Google github.com/Microsoft/TypeScript/issues/7960

1 votes

Une raison pour laquelle vous ne pouvez pas étendre un générique Fruit avec l'interface isDecayed et déclare ensuite que les fruits sont de type Fruit[] ?

0 votes

Étrange... il n'y a pas d'erreur si vous définissez explicitement l'élément key à string i.e const key: string = Math.random() > 0.5 ? 'apples': 'pears';

89voto

Sefe Points 11

TypeScript supporte le typage structurel (é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 qui n'a que les éléments suivants isDecayed: boolean membre. En raison du typage structurel, vous n'avez pas besoin d'hériter de Apple et Pear à partir d'une telle interface.

Il existe différentes façons d'attribuer un tel type de compatibilité :

Attribution du type lors de la déclaration de la variable

Cette déclaration est implicitement typée en Apple[] | Pear[] :

const fruits = fruitBasket[key];

Vous pouvez simplement utiliser un type compatible de manière explicite dans votre déclaration de variable :

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

Pour une meilleure réutilisation, vous pouvez également définir le type d'abord et l'utiliser ensuite dans votre déclaration (notez que l'attribut Apple et Pear les interfaces n'ont pas besoin d'être modifiées) :

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

Cast au type compatible pour l'opération

Le problème avec la solution donnée est qu'elle modifie le type de l'objet de l'enquête. fruits variable. 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 redonner au tableau le même type que celui de la variable fruits :

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

Ou avec la réutilisable Fruit type :

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 les deux, fruits et freshFruits sera de type Apple[] | Pear[] .

7voto

shusson Points 2369

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

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

2 votes

Je n'ai pas un assortiment de pommes et de poires mais un assortiment de pommes ou un assortiment de poires.

1 votes

La même raison pour laquelle j'utilise Typescript : Pour structurer mon code, pour que moi ou une tierce personne comprenne mieux ce que fait le code et avec quoi il est traité. Et un tableau mixte de pommes et de poires n'est pas ce qu'il y aura dans ce tableau. Alors pourquoi devrais-je le définir comme un tableau mixte ?

0 votes

Corrigez-moi si je me trompe, mais l'affirmation selon laquelle le tableau contiendra soit des pommes, soit des poires n'est pas fausse, elle ne décrit simplement pas toute la vérité.

4voto

Joe Frambach Points 13853

Peut-être créer un Fruit qui fournit isDecayed. fruits est maintenant de type Fruit[] afin que le type puisse être explicite. Comme ceci :

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

0 votes

Merci - C'est la solution de rechange que j'ai utilisée entre-temps, mais cela ressemble plus à un piratage qu'à une véritable solution pour moi. Est-ce le seul moyen ?

0 votes

Cela arrive tout le temps. Votre FruitBasket pourrait être juste un tableau de Fruit s aussi

-3voto

Bujdea Bogdan Points 73

J'ai eu le même problème avec numeral, une bibliothèque JS. La solution a été de réinstaller les typages avec cette commande :

npm install --save @types/numeral

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X