結論
const a = [1, 2, 3, null]; // nullとundefinedをはじく const filtered = a.filter(<T>(n: T): n is NonNullable<T> => n != null); // nullだけはじく const filtered2 = a.filter(<T>(n: T): n is Exclude<T, null> => n !== null);
nullを除いた配列の型
nullを許容する配列があったとします。この配列にfilterをかけてnullをはじくコードは以下のようになります。
const a = [1, 2, 3, null]; const filtered = a.filter(n => n != null);
しかし、これだけではfilteredの型は(number | null)[]
になります。filteredの型をnumber[]
型にするにはasかisを使います。
const filtered = a.filter(n => n != null) as number[]; const filtered = a.filter((n): n is number => n != null);
これだけでもある程度は十分です。しかし、オブジェクトの配列であった場合、これだけでは不十分な場合があります。
type Obj = { hoge: string; fuga: number; }; type ExObj = Obj & { piyo: boolean }; const objs: (Obj | ExObj | null)[] = [ { hoge: "hoge", fuga: 3 }, { hoge: "hoge", fuga: 3, piyo: true }, null, ]; const filtered = objs.filter((n): n is Obj => n != null); // Obj[]型になって情報が失われる!
というわけで、もっときちんと型をつけましょう。
コード解説
const a = [1, 2, 3, null]; const filtered = a.filter(<T>(n: T): n is Exclude<T, null> => n != null);
ExcludeはUnion型Tを受け取り、TからUを除いたUnion型を返します。たとえばExclude<string | number | null, null>
はstring | number
です。NonNullable<t>
はTからnullとundefinedを除いた型を返します。
Exclude、NonNullableを使っているため、コードの意味もnullを除いたUnion型の手書きよりハッキリしています。
参考
参考というか、このページをもとに改良したのがこの記事です。