I am trying to narrow down (w/ inference) the type that I want out of this array filter, but it's giving me an TypeError: 'Item' is missing the following properties
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)
Any ideas?
Unfortunately the compiler isn't smart enough to look at the
l => "name" in l
callback and understand that it can be used to narrow anItem | ItemGroup
to just anItemGroup
. Luckily, you can tell the compiler that this is the intent by annotating it as a user-defined type guard function:Now if you call
isItemGroup(l)
and the result istrue
, the compiler will understand thatl
is anItemGroup
. Additionally, the standard library provides a call signature forArray.prototype.filter()
that accepts a user-defined type guard callback and produces a narrowed array. So by usingisItemGroup
as the callback, you get your desired outcome:Playground link to code