TypeScript: filter で型が絞れない時は flatMap を使うと良いかも
March 1, 2022
こんな配列があるとして、
const items = ['hello', null, 'world'];
こうしても、残念ながら型はうまくフィルタリングされません。
const filtered = items.filter((item) => item !== null);
// filtered: (string | null)[]
ですので、Type Guard を使ってこうするのが一般的だと思うのですが、
const filtered2 = items.filter((item): item is NonNullable<typeof items[number]> => item !== null);
// filtered2: string[]
記述が長くなるのと、Type Guard は結局アサーションしているのと同じなので、別の書き方はないものかと思うものです。
そんなときに見つけたのがこの書き方。
const filtered3 = items.flatMap((item) => item ?? []);
// filtered3: string[]
ちょっとテクニカルですがアサーション不要で機能していて良いですね。
ちなみに flatMap()
は map()
して flat()
しているのと同じなので以下でも同じです。
const filtered4 = items.map((item) => item ?? []).flat();
// filtered4: string[]
TypeScript の型の話からはちょっとずれますが、深さが第二階層以上の配列をフラット化したい場合は flat()
と組み合わせて使いましょう。
flatMap()
は第二階層以上をフラット化しませんが、 flat()
ならば、引数に数値を渡すことで、どの深さまでフラット化するのか指定することができます。
const moreItems = ['hello', null, ['hello', null], [['hello', null]], [[['hello', null]]]];
const filtered5 = moreItems.flat(3).flatMap((item) => item ?? []);
// filtered5: string[]