この投稿はTypeScriptで(string | undefined)[]
のようなstring
とundefined
が入る配列からundefined
を取り除く処理をfilter
メソッドで書くとき、filter
メソッドが返す型をstring[]
にする方法を紹介します。
問題点
次のようなstring
とundefined
が入り混じった配列があり、
const items: (string | undefined)[] = ['a', undefined, 'b', undefined, 'c']
ここからundefined
だけを取り除きstring
だけの配列を作りたいとします。
Array.prototype.filter
を使うと、1行でその処理が実装できるのはご存知かと思います:
const stringItems = items.filter(item => item !== undefined)
// or
const stringItems = items.filter(item => typeof item === 'string')
処理自体はこれでいいのですが、問題点が1つあります。それは、stringItems
の型がstring[]
にならず、元配列の(string | undefined)[]
のままになるという点です。このせいで、stringItems
をstring[]
だと思って処理しようとするコードはコンパイルエラーになってしまいます:
解決策: ユーザ定義タイプガードを使う
filter
の戻り値をstring[]
に型付けするにはどうしたらいいのでしょうか?
解決策のひとつは、filter
に渡す関数をユーザ定義タイプガード関数にすることです。普通の関数との書き方の違いは、戻り値の型をboolean
ではなく、引数名 is 型
にする点です。
今回の例では型は string[]
に揃えるので typeof item == 'string'
の方法を採用します。
// 普通の関数
const f1 = (item): boolean => typeof item == 'string'
// ユーザ定義タイプガード関数
const f2 = (item): item is string => typeof item == 'string'
ユーザ定義タイプガード関数の詳細は他ドキュメントをご参照ください。
このユーザ定義タイプガード関数をfilter
に渡してあげると、filter
はstring[]
型を返すものとコンパイラに解釈させることができます:
const stringItems = items.filter((item): item is string => typeof item == 'string')
これにより、先程コンパイルエラーになっていたコードの問題も解決されます:
Playground Link
おまけ: 「取り除く」という意味合いにする
上記の解決策で問題自体は解消しましたが、filter(item => typeof item == 'string')
の部分が「undefined
を取り除く」というより「string
に絞り込む」という意味合いのコードになっているので、おまけとして「取り除く」という意味になるコードも考えてみたいと思います。
undefined
を取り除くということは、string | undefined
ならstring
に、string | number | undefined
ならstring | number
になるべきです。そういう型のマッピングをするのに便利なのがExclude<T,U>
型です。
これを使って「取り除く」路線で実装したコードが次になります:
const stringItems = items.filter(
(item): item is Exclude<typeof item, undefined> => item !== undefined
)
ちなみにこの実装であれば、items
が(string | undefined)[]
から、(string | number | undefined)[]
になったとき、filter
が返す型もそれに追従して、(string | number)[]
になるといった仕掛けになります:
const items: (string | number | undefined)[] = ['a', undefined, 'b', undefined, 'c']
const stringOrNumbers: (string | number)[] = items.filter(
(item): item is Exclude<typeof item, undefined> => item !== undefined
)
最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローしてもらえると嬉しいです→Twitter@suin