🦦
配列のfilter()の型対応にtypesafe-utilsを使う
TypeScriptで配列のfilterを使用する場合を考える
// itemsは (string|number)[]
const items = [1, 2, "foo", "baz"]
const stringItems: string[] = items.filter(item => typeof item === "string")
stringItemsがstring[]になってくれることを期待したいところだが、これは(string|number)[]のままだ。
これを解決するにはType Guardを使う必要がある
const items = [1, 2, "foo", "baz"]
const isString = (item): item is string => {
return typeof item === "string"
}
const stringItems: string[] = items.filter(isString)
もしくはこう
const stringItems = items.filter((item): item is string => {
return typeof item === "string"
})
また一方で思い切ってasや@ts-ignoreを使ってしまうというのも一つの考え方だろう
// @ts-ignore
const stringItems: string[] = items.filter(item => typeof item === "string")
const stringItems: string[] = items.filter(item => typeof item === "string") as string[]
課題
Type Guardは単なる宣言のため、誤った情報を返しうる。
const items = [1, 2, "foo", "baz"]
const isString = (item): item is string => {
// 何らかの不幸な改修でこうなってもTypeScriptは検知しない
return typeof item === "string" || item === "number"
}
const stringItems: string[] = items.filter(isString)
これを避けるにはTypeGuardの関数を入念にテストするなどしかないだろう。
実際filterを使うケースはnullを除外するなどがせいぜいと考えると、割に合わなくて面倒に感じるケースも多い。
解決策
そういう場合はtypesafe-utilが便利な選択肢になりうるだろう。
あまりスター数も現時点で多くないライブラリではあるものの、isString,isTrue,isTruthy,isNumberなど一通りのものが揃っており型がありテストも一通り行われている。
例えば先程実装したisStringであればこんな具合だ。
import { isString } from 'typesafe-utils'
const items = [1, 2, "foo", "baz"]
const stringItems = items.filter(isString)
もちろんあまり複雑なことをやるなら自前しなければならないが、せいぜいnull避け程度なら十分に価値を発揮してくれるはずだ
Discussion