かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

TypeScript Array.filter の型もいい感じにフィルタリングしたい

API から取得したデータが string | undefined のリストだったので filtertypeof === 'string' のものだけにしたのだけフィルター後の配列の型がうまく行かなかったのでメモ

問題: 型情報がフィルタリングされない

const data = ['星宮いちご', '霧矢あおい', undefined, '紫吹蘭', undefined];
const res = data.filter((item) => typeof item === 'string');
// res: (string | undefined)[]
console.log(res);
// => ['星宮いちご', '霧矢あおい', '紫吹蘭']

フィルター後の res の値は string[] だが、推論される型情報は const res: (string | undefined)[] のままになってしまう

Type Guard を使う

フィルターで実行されるコールバック関数を Type Guard にすれば返される配列の型を限定できる

const data = ['星宮いちご', '霧矢あおい', undefined, '紫吹蘭', undefined];

const isString = (value: unknown): value is string => typeof value === 'string';
const res = data.filter(isString);
// res: string[]

又は直接 Type Guard を書いてしまう

const data = ['星宮いちご', '霧矢あおい', undefined, '紫吹蘭', undefined];
const res = data.filter((item): item is string => typeof item === 'string');
// res: string[]

これでフィルター後の resstring[] になる

Type Guard の問題

T is X の Type Guard はエンジニアがコンパイラに型を教えているので、Type Guard の実装を間違えると推論される型と実際の型が異なる結果なるリスクが有る

const data = ['星宮いちご', '霧矢あおい', undefined, '紫吹蘭', 0, 2];

const isString = (value: unknown): value is string => {
  return typeof value === 'number' || typeof value === 'string';
};
const res = data.filter(isNumber);
// const res: string[]
// res => ['星宮いちご', '霧矢あおい', '紫吹蘭', 0, 2]

上記の Type Guard 関数 isStringnumber の値も通してしまっているが返り値が value is string なので返され値は全て string 型だと判定されてしまう

typesafe-util を使う

typesafe-utilはいろんなメソッドの TypeScript の型をいい感じにしてくれるライブラリ
このライブラリで提供されている filter を使えば自身で Type Guard を書かなくても済む

import { isString } from 'typesafe-utils';

const data = ['星宮いちご', '霧矢あおい', undefined, '紫吹蘭', 0, 2];
const res = data.filter(isString);
// res: string[] => ['星宮いちご', '霧矢あおい', '紫吹蘭']

所感

TypeScript の 配列操作の型にハマることが多いです…
簡単な箇所なら自前で書いてしまってもいいし filter を多く使うのならライブラリを使ってしまうのが簡単そうだと思いました。

TypeScript ちから高くなりてぇょ…
おわり


[参考]