📘

【TypeScript】配列の型を絞り込むときはflatMapがおすすめ

2022/12/28に公開3

こんにちは。
スペースマーケットでフロントエンドエンジニアをしているmizukiです!

今回はタイトルの通り、配列で型を絞る際にflatMapを使うのがおすすめだよ!といった話をします。

配列から特定のデータを排除する

例えば、配列からnullableな値を消したいとします。
その場合は以下のようなコードになります。

const nullableArray: Array<string | null> = ['hoge', null, 'fuga']

const array = nullableArray.filter((data) => {
  return !!data
})

ただ、この場合だと型定義としてはnullが排除されたことを認知できず、変数arrayの型は引き続きArray<string | null>と認識されてしまいます。

isを使って型を絞り込む

先ほどの場合、期待値としては変数arrayはArray<string>となって欲しかったと思います。
そこで、isを使って型を指定することで型定義からもnullableな値を排除することができます。

const nullableArray: Array<string | null> = ['hoge', null, 'fuga']

// isを使ってdataに型を当てる
const array = nullableArray.filter((data): data is string => {
  return !!data
})

こうすることでarrayがArray<string>となってくれます。

flatMapを使う

これでできてるからisでいいじゃん!となるのですが、isで型を当てた場合は絞り込みの処理に関係なく強制的に型が当たってしまいます。
そのため、以下のように間違った絞り込み方をしてしまった場合に、データが間違って絞られているものの、そのバグに気づきにくくなってしまいます。

const nullableArray: Array<string | null> = ['hoge', null, 'fuga']

const array = nullableArray.filter((data): data is string => {
  return !data  // 誤った処理をしてしまう
})
console.log(array) // => 型定義は Array<string> だが実際のデータでは [null] となってしまう

そこで便利なのがflatMapです。
そもそもflatMapとは、2次元配列を1次元配列に直したいときなどに使用できます。
配列の中にデータがあればそのデータを展開し、空配列の場合はデータを返さずに新しい配列を作成します。

const array = ['hoge', ['fuga', 0], []]

const newArray = array.flatMap((data) => {
  return data
})
console.log(newArray) // => ['hoge', 'fuga', 0]

その機能を使って、型を絞り込みます。

const nullableArray: Array<string | null> = ['hoge', null, 'fuga']

const array = nullableArray.flatMap((data) => {
  return data ?? []
})

上記のように、dataがnullableな値だった場合に空配列をreturnするようにすれば新たに生成する配列からは排除され、変数arrayは型定義も実際のデータもArray<string>にすることができます。
もし仮に、以下のように実際の処理を間違えたとしても

const nullableArray: Array<string | null> = ['hoge', null, 'fuga']

const array = nullableArray.flatMap((data) => {
  return !data ?? : [] // => 間違った記述をしてしまう
})

これだとそもそもarrayの型定義がArray<string>にはならないため、記述のミスに気づくことができます。
また、加えて変数定義時にconst array: Array<string> = ...と期待値を明記しておくとより記述ミスに気付きやすいかと思います。

まとめ

今回は配列の型を絞り込む時にflatMapがおすすめだよ、といった話をしました。
おすすめですとは言ったものの、最終的にできることは同じと言えば同じなので、会社の慣習などと帳尻を合わせながら使っていくのが良いかなと思います。

おわりに

スペースマーケットでは一緒に働ける仲間を募集しています!
カジュアル面談も盛んに行なっておりますので、ちょっと話聞きたい程度であってもお気軽にお問い合わせください!

https://www.wantedly.com/projects/1113570

https://www.wantedly.com/projects/1113544

https://www.wantedly.com/projects/1061116

https://spacemarket.co.jp/recruit/engineer/

スペースマーケット Engineer Blog

Discussion

hr20k_hr20k_

おもしろいですね、知らないハックでした。

わたしは下記の関数を用意しておいて null 除去をすることが多いですが、用意してないときは flatMap 使ってみます。(関数として用意しておけば間違った絞り込みはしないはず。。)

const nonNullable = <T>(value: T): value is NonNullable<T> =>
  value !== null && typeof value !== 'undefined';

// 使用時
const nonNullArray = ['hoge', null, 'fuga'].filter(nonNullable);
mizukimizuki

コメントいただきありがとうございます!
少しでも参考になっていましたら幸いです!

わたしは下記の関数を用意しておいて null 除去をすることが多いです

関数を定義しておくのは便利ですね!僕も参考になりました🙇‍♂️

ゆうたゆうた

この記事の方法は現時点では推奨できません。
flatMapでfilterのような処理をする事は普通考えないはずなので可読性が低下します。

TypeScript 5.5でfilterを利用すると型の絞り込みが出来ます。
今の時代はTypeScript5.5以上を利用してfilterを利用するべきです。

時代が変わった今は、この記事の内容はTypeScript 5.5未満であれば利用することを検討するぐらいの内容です。

※筆者を悪く言っているのではなく、古い記事ですが未だにGoogle検索して上位に出てくるのでコメントしています。