webとモバイルアプリの逆転

webアプリとモバイルアプリの開発に関する話や本・ゲームなどの趣味の話を雑多にしていきたい

TypeScriptで配列が含まれているUnion型の変数に対してType Guardを用いて変数を配列として扱う方法

TL;DR

(追記) Array.isArrayを使えば問題なさそう

Union型に対して配列としての処理を記述したい場合、ユーザー定義のTypeGuardをすればよかった。
ユーザー定義のやり方は、返り値の型を<引数> is <配列>とし求めている型ならtrueが返るように関数を記述することで目的を達成できた。

詳細

TypeScriptで配列が含まれているユニオン型に対して配列操作を行おうとした際、 コンパイラが以下のエラーを吐いてしまいうまく動かすことができませんでした。

Property 'map' does not exist on type 'string | string[]'.  Property 'map' does not exist on type 'string'.

原因としてはユニオン型は複数の型を複合させた型であるため 配列のみに存在するメソッドを使おうとしても他のメソッドでは存在しないことでした。 この問題を解決するための方法を調べたところ、Type Guardで変数を配列として扱うという方法を見つけたのでその方法をメモします。

まず、Type Guardのやり方を調べたところ以下の5つの方法がありました。(参考: 型ガード - TypeScript Deep Dive 日本語版

  • typeof
  • instanceof
  • in
  • リテラル型のType Guard
  • ユーザー定義のType Guard

ただ、上記の4つでは配列として扱うことができませんでした。
typeofではobjectとして認識されてしまい目的を達成できませんでした。
instanceofはArrayとして認識することまではできたのですが、配列の中身の型をうまく認識できなかったので断念しました。 もしかしたらうまくできるやり方あるのかもしれないです。
inはオブジェクト内のプロパティの存在を安全にチェックするものなので配列の認識には使えませんでした。 リテラル型のType Guardはリテラル型の判定なのでもちろん使えません。

最後のユーザー定義のTypeGuardを使うことで配列をうまく認識することができました。
やり方は簡単で、関数の返り値を<引数> is <配列>として、求めている型だったらtrueが返すことで配列の認識にすることができました。 今回、stringの配列を認識するために書いた定義は以下のようになりました。

function isStringArray(arg: any): arg is string[] {
    return typeof arg[0] === "string";
}

TypeScriptの型はまだまだしらないことがあるのでもっとDeepDiveしていきたいなと思いました。