Подтвердить что ты не робот

Невозможно вызвать выражение, тип которого не имеет сигнатуры вызова

У меня есть яблоко и груши - оба имеют атрибут 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 говорит мне:

Невозможно вызвать выражение, тип которого не имеет сигнатуры вызова. Type '((callbackfn: (значение: Apple, индекс: число, массив: Apple []) = > any, thisArg?: any) = > Apple []) |... 'не имеет совместимых сигнатур вызовов.

Что здесь не так - это просто, что Typescript не любит свежие фрукты или это ошибка Typescript?

Вы можете попробовать это самостоятельно в официальном Typescript Repl.

4b9b3361

Ответ 1

TypeScript поддерживает структурную типизацию (также называемую утиной печатью), что означает, что типы совместимы, когда они имеют одни и те же элементы. Ваша проблема в том, что 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[].

Ответ 2

Возможно, создайте общий интерфейс 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);

Ответ 3

Как упоминалось в проблема github, первоначально связанная @peter в комментариях:

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