#typescript #types #typescript-typings #union-types
#typescript #типы #typescript-типизации #типы объединения
Вопрос:
Я пытаюсь сузить (без вывода) тип, который я хочу исключить из этого фильтра массива, но он выдает ошибку типа: «Item» отсутствуют следующие свойства
type ItemList = (Item | ItemGroup )[];
type ItemGroup = {
name: string;
items: Item[];
}
type Item = {
key: string;
label: string;
}
const list: ItemList = [
{
key: 'key',
label: 'label'
},
{
name: 'name',
items: [
{
key: 'key1',
label: 'label2'
},
{
key: 'key3',
label: 'label4'
},
]
}
]
const groups: ItemGroup[] = list.filter( l => 'name' in l )
^^^^^^
// Type '(Item | ItemGroup)[]' is not assignable to type 'ItemGroup[]'.
// Type 'Item | ItemGroup' is not assignable to type 'ItemGroup'.
// Type 'Item' is missing the following properties from type 'ItemGroup': name, items ts(2322)
Есть идеи?
Комментарии:
1. похоже
groups
, что он должен быть введен какItemList
, потомуfilter
что возвращает массив. Сообщение об ошибке на самом деле сообщает вам, в чем проблема:name
для вашего нетItemGroup
.2. извините, я пропустил, что когда я писал вопрос, он должен быть
ItemGroup[]
(обновляю его сейчас)
Ответ №1:
У вас есть массив, который содержит оба Item
ItemGroup
элемента и . Вы хотите отфильтровать этот массив только для тех элементов, которые есть ItemGroup
, и вы хотите, чтобы typescript понимал, что вы отфильтровали список, и знал, что возвращаемый тип есть ItemGroup[]
.
Как вы можете этого добиться, так это превратить фильтр l => 'name' in l
в собственную функцию защиты типов. Возвращаемый тип value is ItemGroup
сообщает typescript «тогда и только тогда, когда это верно, значение имеет тип ItemGroup».
const isItemGroup = (value: Item | ItemGroup): value is ItemGroup => 'name' in value;
const groups: ItemGroup[] = list.filter( isItemGroup );
Используя защиту типа, typescript может понять значение list.filter
, и ваша ошибка исчезнет.
Комментарии:
1. Это также работает
const groups = list.filter((value): value is ItemGroup => 'name' in value);
Ответ №2:
К сожалению, компилятор недостаточно умен, чтобы посмотреть на l => "name" in l
обратный вызов и понять, что его можно использовать для сужения an Item | ItemGroup
до простого an ItemGroup
. К счастью, вы можете сообщить компилятору, что это намерение, аннотируя его как определяемую пользователем функцию защиты типа:
const isItemGroup = (l: Item | ItemGroup): l is ItemGroup => "name" in l;
Теперь, если вы вызываете isItemGroup(l)
и результат есть true
, компилятор поймет, что l
это an ItemGroup
. Кроме того, стандартная библиотека предоставляет сигнатуру вызова Array.prototype.filter()
, которая принимает определяемый пользователем обратный вызов типа guard и создает суженный массив. Таким образом, используя isItemGroup
в качестве обратного вызова, вы получаете желаемый результат:
const groups: ItemGroup[] = list.filter(isItemGroup); // no error now
Комментарии:
1. вы опередили меня в публикации на 38 секунд!
2. @LindaPaiste На секунду я подумал, что дважды опубликовал один и тот же ответ, поскольку они очень похожи. 🔮
3. Ах, хорошо, это имеет смысл. В то же время я использовал функцию уменьшения Lodash и просто проверял, что следующее значение содержит реквизит ‘name’. Мне нравится эта функция, спасибо!
Ответ №3:
Вы можете утверждать, что ваш отфильтрованный результирующий массив имеет тип ItemGroup[]
, используя утверждение типа:
const groups: ItemGroup[] = list.filter( l => 'name' in l ) as ItemGroup[]