🦦

配列のfilter()の型対応にtypesafe-utilsを使う

2021/06/09に公開

TypeScriptで配列のfilterを使用する場合を考える

// itemsは (string|number)[]
const items = [1, 2, "foo", "baz"]
const stringItems: string[] = items.filter(item => typeof item === "string")

stringItemsstring[]になってくれることを期待したいところだが、これは(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避け程度なら十分に価値を発揮してくれるはずだ

GitHubで編集を提案

Discussion