TypeScript の enum (enum)
列挙型(Enum)は、実行時の挙動を持つ数少ないTypeScriptの型機能の一つです。これらは型であると同時に、実際のJavaScriptオブジェクトも生成します。
1. 数値による列挙
(1) 基本的な構文
TYPESCRIPT
enum Direction {
Up, // 0(Auto-increment from 0)
Down, // 1
Left, // 2
Right // 3
}
let dir: Direction = Direction.Up;
console.log(dir); // 0
console.log(Direction[0]); // "Up"(Inverse Mapping)
(2) カスタム初期値
TYPESCRIPT
enum Status {
Active = 1, // 1
Inactive, // 2(Auto-increment)
Pending // 3
}
enum HttpStatus {
OK = 200,
Moved = 301,
BadRequest = 400,
NotFound = 404,
ServerError = 500
}
let code: HttpStatus = HttpStatus.OK;
console.log(code); // 200
(3) 数値列の逆マッピング
コンパイル後、数値列挙型は双方向マッピングオブジェクトを生成します。これにより、名前から値を取得したり、値から名前を取得したりすることができます。
TYPESCRIPT
enum Role {
Admin = 0,
Editor = 1,
Viewer = 2
}
// Forward Mapping: name → value
console.log(Role.Admin); // 0
// Inverse Mapping: value → name
console.log(Role[0]); // "Admin"
console.log(Role[1]); // "Editor"
コンパイル後に生成されたJavaScript:
JAVASCRIPT
var Role;
(function (Role) {
Role[Role["Admin"] = 0] = "Admin";
Role[Role["Editor"] = 1] = "Editor";
Role[Role["Viewer"] = 2] = "Viewer";
})(Role || (Role = {}));
2. 文字列の列挙
(1) 基本的な構文
文字列列の各要素には明示的に値を代入する必要があります。自動インクリメントは行われません:
TYPESCRIPT
enum EventType {
Click = "click",
Change = "change",
Submit = "submit",
Focus = "focus"
}
let event: EventType = EventType.Click;
console.log(event); // "click"
(2) 文字列の列挙には逆写像が存在しない
TYPESCRIPT
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
console.log(Color.Red); // "RED" ✅ Forward Mapping
// console.log(Color["RED"]); // undefined ❌ String enumeration has no reverse mapping
💡 理由: 数値型列挙型の値は数値であり、オブジェクトのキーとして使用できます。一方、文字列型列挙型の値は文字列であるため、オブジェクトのキーと競合し、逆マッピングを実装することができません。
▶ Example: Defining Order Statuses Using an Enumeration
TYPESCRIPT
enum OrderStatus {
Pending = "PENDING",
Processing = "PROCESSING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED"
}
function getStatusLabel(status: OrderStatus): string {
switch (status) {
case OrderStatus.Pending: return "Pending";
case OrderStatus.Processing: return "Processing...";
case OrderStatus.Shipped: return "Shipped";
case OrderStatus.Delivered: return "Delivered";
case OrderStatus.Cancelled: return "Canceled";
}
}
let currentStatus: OrderStatus = OrderStatus.Processing;
console.log(`Current Status:${getStatusLabel(currentStatus)}`);
console.log(`Status Code:${currentStatus}`);
出力:
TEXT
Current Status:Processing...
Status Code:PROCESSING
3. 異種列挙
列挙型には数値と文字列の値を混在させることができますが、これは推奨されません:
TYPESCRIPT
enum Mixed {
No = 0,
Yes = "YES"
}
⚠️ 推奨されません: 異種混在の列挙型は混乱を招きやすいため、TypeScriptの公式ドキュメントでもその使用を避けるよう推奨されています。実際の開発では、数値型の列挙型のみ、あるいは文字列型の列挙型のみを使用するようにしてください。
4. const 列挙型
const enum コンパイル時にインライン式に置換されます。JavaScript オブジェクトは生成されないため、パフォーマンスは向上しますが、機能は制限されます:
(1) 基本的な構文
TYPESCRIPT
const enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
let c = Color.Red;
// After compilation:let c = "RED"(Direct Inline Replacement,No enumeration objects)
(2) const 列挙の制限事項
TYPESCRIPT
const enum Direction {
Up = "UP",
Down = "DOWN"
}
// ❌ const Enumerations cannot use reverse mapping.
// console.log(Direction[0]);
// ❌ const Enumerations cannot be iterated over at runtime
// for (let d in Direction) { }
// ✅ Only simple value references are allowed.
let dir = Direction.Up; // Compile to let dir = "UP"
(3) preserveConstEnums オプション
const 列挙型をインライン展開せず、列挙オブジェクトを保持したい場合は、preserveConstEnums: true を有効にしてください:
TYPESCRIPT
// tsconfig.json
// "preserveConstEnums": true
const enum Status {
Active = 1
}
let s = Status.Active;
// After compilation:Enumeration objects are still generated(But quotes are also inlined.)
5. 列挙型の実行時および型安全性
(1) 型としての列挙型
TYPESCRIPT
enum Role {
Admin,
Editor,
Viewer
}
function checkAccess(role: Role): boolean {
return role === Role.Admin || role === Role.Editor;
}
checkAccess(Role.Admin); // ✅
checkAccess(0); // ✅ Numeric enumerations accept numeric values(but ⚠️ not recommended)
// checkAccess(99); // ✅ Compilation successful!Any number can be converted to——This is a numeric enumeration vulnerability.
🔥 数値列挙型の落とし穴: 数値列挙型の変数には、どのような数値でも代入できます。たとえば、
let r: Role = 99 も有効な値です。これは、数値列挙型がビットフラグを念頭に置いて設計されているためです。厳密な値の制約が必要な場合は、文字列列挙型またはリテラル共用体型を使用してください。
(2) 文字列の列挙の方が安全である
TYPESCRIPT
enum Role {
Admin = "ADMIN",
Editor = "EDITOR",
Viewer = "VIEWER"
}
let r: Role = Role.Admin; // ✅
// r = "ADMIN"; // ❌ String enumeration does not accept regular strings.
// r = "SUPERADMIN"; // ❌ Only enumeration members are accepted
6. 列挙型とリテラル和集合型
| プロパティ | 列挙型 | リテラル和集合型 |
|---|---|---|
| 実行時コード | はい(オブジェクトが生成される) | いいえ(純粋型) |
| リバースマッピング | デジタル列挙:はい | いいえ |
| すべての値を順に処理する | はい | いいえ |
| 値の制約 | 数値列挙(緩い) | 厳密 |
| コードヒント | 列挙型のメンバーのオートコンプリート | ユニオンの値のオートコンプリート |
| バンドルのサイズ | コードサイズの増加 | サイズゼロ |
| JS との相互運用性 | 列挙値はカスタムオブジェクト | ネイティブの文字列/数値 |
(1) 列挙型の使用例
- 逆マッピング(値 → 名前)が必要です
- すべての値は実行時に反復処理されなければならない
- 列挙型は、実行時オブジェクト(API の定数マッピングなど)として使用するために必要です。
(2) リテラル和集合型を使用したシナリオ(第一候補として推奨)
- 必要なのは型制約のみです
- ネイティブのJavaScript文字列および数値との相互運用性が求められる
- バンドルサイズをできるだけ小さくすることを目指す
- 実行時の動作は不要
▶ 例:2つの手法の比較
TYPESCRIPT
// Method 1:Enumeration
enum Direction1 {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
// Method 2:Literal Union Types(Recommendations)
type Direction2 = "UP" | "DOWN" | "LEFT" | "RIGHT";
// The two are equivalent in terms of type constraints.
function move1(dir: Direction1): void { console.log(dir); }
function move2(dir: Direction2): void { console.log(dir); }
move1(Direction1.Up); // ✅ "UP"
move2("UP"); // ✅ Use the string directly,Even simpler
// But enumerations can be iterated over
console.log(Object.values(Direction1));
// ["UP", "DOWN", "LEFT", "RIGHT"]
// Literal union types cannot be iterated over(Pure compile-time types)
❓ よくある質問
Q 列挙型を使うべきでしょうか?
A TypeScriptコミュニティの意見は二分されています。一方は、列挙型はTypeScript独自の機能であり、使用すべきだと考えています。もう一方は、リテラル和集合型の方が軽量で推奨されると考えています。実用的なアドバイス:型制約のみが必要な場合(ほとんどの場合がこれに該当します)はリテラル和集合型を使用し、実行時の挙動(反復処理や逆マッピングなど)が必要な場合は列挙型を使用してください。
Q なぜ数値列挙型は任意の数値を受け入れるのですか?
A これはTypeScriptの設計上の決定です。数値列挙型はビットフラグ (
enum Perm { Read = 1, Write = 2, Execute = 4 }) をサポートしており、ビットの組み合わせ Read | Write = 3 は有効ですが、列挙型の定義には含まれていません。そのため、数値列挙型では制約が緩和されています。この挙動を望まない場合は、文字列列挙型またはリテラルユニオン型を使用してください。Q
const 列挙型と通常の列挙型の違いは何ですか?A
const 列挙型はコンパイル時にインライン化されます。つまり、Color.Red は直接 "RED" に置き換えられ、列挙型オブジェクトは生成されません。メリットは、バンドルサイズが小さくなり、実行時のパフォーマンスが向上することです。デメリットは、逆マッピングができず、反復処理ができず、動的なコンテキストで使用できないことです。実行時の機能が必要でない限り、const列挙型の使用を優先してください。Q 列挙型はインターフェースや型エイリアスと組み合わせることができますか?
A はい。列挙型の値は、インターフェースのプロパティ型として使用できます—
interface Config { role: Role }。また、列挙型のメンバは、共用体のメンバとしても使用できます—type Mixed = Role.Admin | "superadmin"。列挙型は型と値を統合した概念であり、柔軟な使い方が可能です。📖 まとめ
- 数値列挙型は自動的に0からインクリメントされ、逆マッピングに対応しています。一方、文字列列挙型は明示的に割り当てる必要があり、逆マッピングには対応していません。
- 異種列挙(数値と文字列の混在)は推奨されません
- Const列挙型はコンパイル時にインライン展開されるため、実行時のオーバーヘッドはゼロですが、機能には制限があります
- 数値型の列挙型は任意の数値を受け入れることができる(設計上の欠陥)一方、文字列型の列挙型はより制限が厳しい
- ほとんどの場合、列挙型ではなくリテラル和集合型を使用することをお勧めします。リテラル和集合型は、軽量で安全であり、メモリを一切消費しないからです。
- 列挙は、値を逆順にマッピングする必要がある場合や、すべての値を順に処理する必要がある場合にのみ使用してください。
📝 練習問題
- 基本問題(難易度 ⭐):文字列の列挙値
Season(春/夏/秋/冬)が与えられたとき、季節に基づいて対応する月の範囲の説明を返す関数を記述してください。 - 上級演習(難易度 ⭐⭐):
const enumを使用して HTTP メソッド(GET/POST/PUT/DELETE)を定義し、次にmethodおよびurlプロパティを含むRequestインターフェースを定義してください。いくつかのリクエストオブジェクトを作成し、型制約が満たされていることを確認してください。 - 課題(難易度:⭐⭐⭐):列挙型の代わりにリテラル和集合型を使用して「権限システム」を実装してください。
Permission = "read" | "write" | "execute" | "admin"を定義し、ユーザーが特定の権限を持っているかどうかを確認するhasPermission(userPerms: Permission[], required: Permission): boolean関数を実装してください(admin にはすべての権限があります)。



