Невозможно вызвать выражение, тип которого не имеет сигнатуры вызова
У меня есть яблоко и груши - оба имеют атрибут 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.
Ответы
Ответ 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);