TypeScript 列舉 (Enum)
列舉 (Enum) 是 TypeScript 提供的一種特殊型別,用來定義一組具名的常數。列舉可以讓程式碼更具可讀性,並且減少使用魔術數字 (magic numbers) 或魔術字串。
數字列舉 (Numeric Enum)
最常見的列舉類型,成員的值自動從 0 開始遞增:
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
let dir: Direction = Direction.Up;
console.log(dir); // 0
// 也可以用數字存取
console.log(Direction[0]); // "Up" (反向映射)
自訂起始值
enum Status {
Pending = 1,
Active, // 2
Completed, // 3
Cancelled, // 4
}
console.log(Status.Active); // 2
自訂每個值
enum HttpCode {
OK = 200,
Created = 201,
BadRequest = 400,
NotFound = 404,
InternalError = 500,
}
function handleResponse(code: HttpCode) {
if (code === HttpCode.OK) {
console.log('成功!');
}
}
handleResponse(HttpCode.OK);
字串列舉 (String Enum)
每個成員都必須用字串初始化:
enum Color {
Red = 'RED',
Green = 'GREEN',
Blue = 'BLUE',
}
let color: Color = Color.Red;
console.log(color); // "RED"
// 字串列舉沒有反向映射
// console.log(Color["RED"]); // undefined
字串列舉的優點:
- 在執行時期有更明確的值,方便除錯
- 輸出的值更具可讀性
- 沒有反向映射,編譯後的程式碼更小
enum LogLevel {
Error = 'ERROR',
Warn = 'WARN',
Info = 'INFO',
Debug = 'DEBUG',
}
function log(level: LogLevel, message: string) {
console.log(`[${level}] ${message}`);
}
log(LogLevel.Error, '發生錯誤!');
// 輸出:[ERROR] 發生錯誤!
異質列舉 (Heterogeneous Enum)
混合數字和字串成員 (不建議使用):
enum Mixed {
No = 0,
Yes = 'YES',
}
異質列舉雖然技術上可行,但會造成混淆,建議避免使用。
常數列舉 (Const Enum)
使用 const enum 可以獲得更好的效能,因為它在編譯時會被完全內聯:
const enum Direction {
Up,
Down,
Left,
Right,
}
let dir = Direction.Up;
// 編譯後:let dir = 0;
普通列舉編譯後會產生一個物件:
// 普通 enum 編譯後
var Direction;
(function (Direction) {
Direction[(Direction['Up'] = 0)] = 'Up';
Direction[(Direction['Down'] = 1)] = 'Down';
Direction[(Direction['Left'] = 2)] = 'Left';
Direction[(Direction['Right'] = 3)] = 'Right';
})(Direction || (Direction = {}));
常數列舉直接內聯值,沒有額外的物件。
const enum 不支援反向映射,也不能用於需要在執行時期動態存取列舉的情況。計算成員與常數成員
列舉成員可以是常數或計算值:
enum FileAccess {
// 常數成員
None,
Read = 1 << 1, // 2
Write = 1 << 2, // 4
ReadWrite = Read | Write, // 6
// 計算成員
G = '123'.length, // 3
}
列舉作為型別
列舉可以當作型別使用:
enum Status {
Active,
Inactive,
}
function setStatus(status: Status) {
console.log(status);
}
setStatus(Status.Active); // OK
// setStatus(2); // 錯誤 (strict 模式下)
聯合列舉與列舉成員型別
當所有列舉成員都是字面量時,每個成員都變成一個型別:
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
// kind 必須是 ShapeKind.Circle
let c: Circle = {
kind: ShapeKind.Circle,
// kind: ShapeKind.Square, // 錯誤
radius: 10,
};
反向映射 (Reverse Mapping)
數字列舉支援反向映射,可以從值取得名稱:
enum Direction {
Up,
Down,
Left,
Right,
}
// 正向:名稱 → 值
console.log(Direction.Up); // 0
// 反向:值 → 名稱
console.log(Direction[0]); // "Up"
console.log(Direction[1]); // "Down"
列舉的替代方案
使用 const 物件
有時候使用 const 物件搭配 as const 是更輕量的選擇:
const Direction = {
Up: 'UP',
Down: 'DOWN',
Left: 'LEFT',
Right: 'RIGHT',
} as const;
// 取得值的聯合型別
type Direction = (typeof Direction)[keyof typeof Direction];
// type Direction = "UP" | "DOWN" | "LEFT" | "RIGHT"
function move(dir: Direction) {
console.log(dir);
}
move(Direction.Up); // OK
move('UP'); // OK
// move("FORWARD"); // 錯誤
使用聯合型別
對於簡單的情況,直接使用字面量聯合型別:
type Direction = 'up' | 'down' | 'left' | 'right';
function move(dir: Direction) {
console.log(dir);
}
move('up'); // OK
// move("forward"); // 錯誤
實際應用場景
API 狀態碼
enum ApiStatus {
Success = 200,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
ServerError = 500,
}
使用者角色
enum UserRole {
Admin = 'ADMIN',
Editor = 'EDITOR',
Viewer = 'VIEWER',
}
function checkPermission(role: UserRole) {
switch (role) {
case UserRole.Admin:
return '完整權限';
case UserRole.Editor:
return '編輯權限';
case UserRole.Viewer:
return '唯讀權限';
}
}
事件類型
enum EventType {
Click = 'click',
Hover = 'hover',
Focus = 'focus',
Blur = 'blur',
}
function addEventListener(event: EventType, handler: () => void) {
document.addEventListener(event, handler);
}
總結
| 列舉類型 | 說明 | 使用場景 |
|---|---|---|
| 數字列舉 | 值自動遞增 | 需要反向映射、位元旗標 |
| 字串列舉 | 明確的字串值 | API 值、日誌級別 |
| 常數列舉 | 編譯時內聯 | 效能要求高、不需反向映射 |