🕌

[TypeScript] .filter((x): x is T => ...) を使わずに配列から特定の型を除外する

2020/10/03に公開1

TypeScript で (string | null)[] 型の配列から null を除外して string[] 型の配列を作るような場合には、array.filter((x): x is string => x !== null) のように戻り値の型を明示するのが一般的ですが、x is T 形式の宣言は戻り値の型検査が行われないのでできれば避けたいところです。

ES2019 で追加された .flatMap() を使うと、x is T を使わず簡単に特定の型の要素のみをフィルタできます。

const array: (string | null)[] = ['a', null, 'b']

const filteredArray: string[] = array.flatMap((x) => (x === null ? [] : [x]))

説明

.flatMap().map().flat() の処理が合わさったメソッドで、callback 関数内で返却した値をフラットに展開して新しい配列を作ります。

;['1', '2'].flatMap((x) => [`${x}-a`, `${x}-b`]) // => ['1-a', '1-b', '2-a', '2-b']

この応用で、callback で空の配列を返却すると要素を削除することもできるので、array.flatMap((x) => (x === null ? [] : [x]))要素が null の場合はその要素を削除し、null でない場合はそのまま残す、という処理を行っているのです。

x === null が false のとき x の型は string なので、callback 関数の戻り値は string[] になり、結果の配列も string[] 型になるのでキャストを使わずに済みます。

Discussion

nap5nap5

勉強になりました。ありがとうございます。サンプルとしてzodのsafeParseでデモ作ってみました。

test("", () => {
  const data: UserPartialData[] = [
    {
      id: "1",
    },
    undefined,
    {
      name: "Benny"
    },
    {
      id: "3",
      name: "Mike"
    },
    null
  ]
  const users = data.flatMap(d => {
    if (d == null) return []
    const parsed = UserSchema.safeParse(d)
    if (parsed.success) return [parsed.data]
    return []
  })
  expect(users).toStrictEqual([
    {
      id: "3",
      name: "Mike"
    },
  ])
})

demo code.

https://codesandbox.io/p/sandbox/confident-maria-rd8wfw?file=/src/index.test.ts:1,1

簡単ですが、以上です。