API から取得したデータが string | undefined
のリストだったので filter
で typeof === '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[]
これでフィルター後の res
は string[]
になる
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 関数 isString
は number
の値も通してしまっているが返り値が 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 ちから高くなりてぇょ…
おわり
[参考]