as を使わずに配列から undefined を 型安全に取り除く


こんにちは kimizuy です。

今回は as で型キャストしないで、配列の要素から undefined を型安全に取り除く方法をご紹介します。

前提

例えば以下のような配列があるとします。

type Person = { name: string; age: number }
// people は (Person | undefined)[] 型
const people = getPeople()

ここから filter(Boolean) を使って undefined の値を実質的に取り除いても型にはまだ undefined が含まれたままです。

const filtered = people.filter(Boolean)
// → (Person | undefined)[]

この配列の中身を取り出して利用する場合、常にオプショナルな値として存在チェックが必要になります。早い段階で中身に加えて型も Person 型のみの配列にしたいですね。

その対策として手軽なのは as を使って型を上書きする方法です。

const filtered = people.filter(Boolean) as Person[]

実際の値では undefined が取り除けているため、これで十分だとも思えますが「as を使うことに躊躇いのない人なんだ…」と周囲に思われるのは気が気ではないですね。

また getPeople() の返す値に変化があった場合は as 部分のメンテナンスもしなければならず、人間の脳内メモリを消費する形で気をつけることが増えてしまいます。

TypeScript の機能を活かしてスマートな方法を模索したいところです。

解決方法

ユーザー定義の型ガードを使います。

ユーザー定義の型ガードとは instanceoftypeof のように型を特定する機能をユーザーが柔軟に定義できる機能です。

以下のように is を使って引数の型を絞り込むことができます。

const existPerson = (person: Person | undefined): person is Person => {
  return !!person
}

const people = getPeople()
const filtered = people.filter(existPerson)
// → Person[]

existPerson()boolean を返す関数です。true だった場合は person is Person 、つまり引数の personPerson 型であることを保障してくれます。

アロー関数で書けば、さらに短く書くこともできます。

const filtered = people.filter((person): person is Person => !!person)

参考

Filtering undefined elements from an array in TypeScript

filter(Boolean) による型推論の機能を追加する動きが昔あったようです)

おわりに

ユーザー定義の型ガードは書くのに面倒なイメージがありましたが、記事としてまとめてみると意外と手軽に書けそうな気がしますね。

以上、お読みいただきありがとうございました。

この機会に、オンラインで気軽に面談してみませんか?

現在弊社では一緒にお仕事をしてくださるエンジニアさんやデザイナーさんを積極募集しています。まずはカジュアルな面談で、お互いに大事にしていることをお話できたらうれしいです。詳しい応募要項は以下からチェックしてください。

パートナー契約へのお問い合わせもお仕事へのお問い合わせも、どちらもいつでも大歓迎です。まずはオンラインでのリモート面談からはじめましょう。ぜひお気軽にお問い合わせください!

お問い合わせしてみる!

投稿者 Gaji-Labo Staff

Gaji-Laboの社内デジタル環境でいろいろなお手伝いをしているがじ専務&じら常務。みんなのシリーズ記事をまとめたり、卒業したスタッフの過去記事を記録したり、Twitterをやったりしています。