列挙型、略してenumは、名前付きの値のセットを定義する方法の一つです。これらの値は、曜日やカードの並びのように、決まった選択肢を表す方法として使われます。
多くのプログラミング言語では、列挙型は特別なデータ型として実装されており、列挙型の各値には一意の整数値が割り当てられています。
デフォルトでは、enumの最初の値には値0が割り当てられ、それ以降の値はそれぞれ1ずつ増加します。しかし、enumの各メンバーに手動で値を割り当てることも可能です。
それではトランプカードのスートを例に見てみましょう。
enum Suit {
Club,
Diamond,
Heart,
Spade
}
console.log(Suit.Club); // 0
console.log(Suit.Diamond); // 1
この例では、Suitという名前のenumを定義し、4つのメンバを定義しています。クラブ、ダイアモンド、ハート、スペードです。デフォルトでは、Clubには値0、Diamondには値1が割り当てられ、以下同様になります。
また、以下のようにenumの各メンバーに手動で値を割り当てることも可能です。
enum Suit {
Club = 1,
Diamond = 2,
Heart = 4,
Spade = 8
}
この例では、Suitの各メンバにそれぞれ1,2,4,8という値が割り当てられています。
enumの一般的な使用例は、switch文の中です。
enum Suit {
Club = 1,
Diamond = 2,
Heart = 4,
Spade = 8
}
let myCard: number = Suit.Club;
switch (myCard) {
case Suit.Club:
console.log("Club");
break;
case Suit.Diamond:
console.log("Diamond");
break;
case Suit.Heart:
console.log("Heart");
break;
case Suit.Spade:
console.log("Spade");
break;
} // "Club"
この例では、switch文がmyCardの値を使用して、実行するcaseを決定しています。case文では、Suitのメンバを使用してmyCardの値と照合しています。
TypeScriptにおけるenumの機能の1つとして、逆マッピングと呼ばれるものがあります。
enum Suit {
Club,
Diamond,
Heart,
Spade
}
console.log(Suit[2]) // "Heart"
上記の例では、enum名(Suit)と値(2)を使って、値2の逆マッピングであるメンバ名Heartにアクセスしています。
対応する値が割り当てられていないインデックスを使ってenumメンバにアクセスしようとすると、undefinedが返されます。これは、TypeScriptのenumの逆マッピング機能が、単純に数値と対応する文字列名をマッピングするためで、そのマッピングが定義されていない場合はundefinedが返されることになる点に注意が必要です。
console.log(Suit[100]) // undefined
このような挙動を避けたい場合は、ユニオン型等を使用するのが良いかもしれません。ただ、完璧にenum型の挙動を再現できない点には注意が必要です。
const Suit = {
Club: "Club",
Diamond: "Diamond",
Heart: "Heart",
Spade: "Spade",
} as const
type Color = typeof Suit[keyof typeof Suit]; // type Suit = "Club" | "Diamond" | "Heart" | "Spade";
console.log(Suit.Club); // "Club"
このコードでは、4つのプロパティを持つSuitオブジェクトを定義しています。クラブ、ダイアモンド、ハート、スペードの4つのプロパティを持ち、それぞれがキー名の文字列値を持つSuitオブジェクトを定義しています。
as constアサーションは、Suitの値が単なる文字列としてではなく、リテラルな文字列値の集合として扱われることを保証するために使用されます。そして、Suitの型を抽出し、keyof演算子とtypeof演算子を用いてColorの型に代入しています。この結果、Color型は文字列リテラル値「Club」、「Diamond」、「Heart」、「Spade」の和集合として定義されることになります。