TypeScript 列舉 (Enum)

Enum (列舉) 是一個從其他語言(如 C# 或 Java)借鏡過來的概念,用來定義「一組命名的常數」。

不過在 TypeScript 中,Enum 的地位比較特殊,因為它不只是型別,它還會產生真實的 JavaScript 程式碼。這讓許多現代開發者對它又愛又恨。

數字列舉 (Numeric Enum)

這是最常見的用法。

enum Direction {
  Up, // 0
  Down, // 1
  Left, // 2
  Right, // 3
}

// 使用
let go: Direction = Direction.Up;

你也可以手動指定值:

enum StatusCode {
  OK = 200,
  NotFound = 404,
  Error = 500,
}

字串列舉 (String Enum)

如果要讓值更有意義(例如方便 debug),可以用字串列舉。

enum Color {
  Red = 'RED',
  Green = 'GREEN',
  Blue = 'BLUE',
}

⚠️ 現代 TypeScript 的爭議:Enum vs Union Types

這是這篇文章最重要的部分。 現在很多資深開發者(包含 TypeScript 官方的一些範例)更傾向使用 Union Types (聯合型別) 而非 Enum

Enum 的問題

  1. 編譯產物:Enum 會被編譯成一個 JavaScript物件 (IIFE),會增加一點點 bundle size。
  2. 行為怪異:數字 Enum 可以被反向查表 (Reverse Mapping),但字串 Enum 不行。

現代替代方案:Union Types

// ✅ 推薦做法:使用字面量聯合型別
type Direction = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT';

function move(dir: Direction) {
  // ...
}

move('UP'); // 直觀、簡單,沒有額外的 JS 產物

現代替代方案:const as const

如果你需要像 Enum 一樣用「物件」來管理常數,可以這樣寫:

// ✅ 推薦做法:Object as const
const Color = {
  Red: 'RED',
  Green: 'GREEN',
  Blue: 'BLUE',
} as const;

// 產生型別: "RED" | "GREEN" | "BLUE"
type Color = (typeof Color)[keyof typeof Color];

// 使用方式跟 Enum 一模一樣
console.log(Color.Red); // "RED"

總結:我該用哪個?

方案優點缺點建議場景
Enum語法簡單,有名字會產生額外 JS 程式碼需要「數字」對應「名字」時 (如 Error Code)
Union Types最輕量,完全不佔空間無法像物件一樣迭代(⭐️ 推薦) 大多數的字串選項
const object輕量且保有物件特性語法稍微囉嗦一點需要物件形式管理常數時

從簡原則:如果你只是需要一組字串選項(如 'primary' | 'secondary'),直接用 Union Types 就好,最乾淨也最快。