🦦
配列の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