Skip to main content

enum 問題與 as const 替代方案

在 TypeScript 開發中,我們經常會使用 enum 來定義常數集合。然而,enum 在某些情況下可能會帶來問題,特別是在處理數字型別以及 TypeScript 5.0 之前的一些行為。本文將探討 enum 的潛在問題,以及為什麼 as const 是更好的替代方案。

🤔 為什麼 enum 有問題?

1. 數字型別的 enum 問題

TypeScript 的 enum 會在編譯時生成 JavaScript 程式碼,這可能導致不必要的運行時開銷:

enum Direction {
Up,
Down,
Left,
Right,
}

編譯後會生成一個物件,增加了程式碼體積,這在只需要型別檢查的場景下顯得冗餘。

數字型別的 enum 在 TypeScript 5.0 之前,型別推斷可能不夠嚴格,導致意外的型別相容性問題。例如,數字型別的 enum 可以與普通數字互換,可能引發 bug。

2. TypeScript 5.0 前的問題

在舊版 TypeScript 中,enum 的型別推斷和檢查行為有時不夠精確:

  • enum 成員可能被推斷為 number 而非具體的數字值,這可能導致型別安全的漏洞
  • enum 的編譯輸出可能在某些場景下(如 tree-shaking)無法被優化,增加了 bundle 大小

3. 運行時行為

enum 會生成運行時程式碼,這對於只需要靜態型別檢查的專案來說可能是不必要的。如果你的專案只關心型別安全而不需要運行時的列舉值,enum 就顯得過重。

✅ 為什麼使用 as const?

使用 as const(const assertion)可以避免 enum 的問題,同時提供更輕量、靈活的型別定義方式。

1. 無運行時開銷

as const 只影響型別層面,不會生成額外的 JavaScript 程式碼,適合需要最小化 bundle 大小的專案:

const Direction = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;

這段程式碼在編譯後保持不變,且 TypeScript 會將其視為唯讀的物件,型別推斷非常精確。

2. 更精確的型別推斷

as const 會將物件的屬性推斷為字面量型別(literal types),而不是廣泛的 stringnumber,這使得型別檢查更嚴格:

type Direction = (typeof Direction)[keyof typeof Direction]; // 得到 0 | 1 | 2 | 3

這種方式比 enum 更靈活,且型別更安全。

3. 靈活性

你可以輕鬆地將 as const 與其他 TypeScript 功能結合,例如 union typeskeyof,而不需要依賴 enum 的特定語法。

🔄 實際範例比較

使用 enum

enum Status {
Success,
Error,
Loading,
}

function handleStatus(status: Status) {
// ...
}

handleStatus(Status.Success); // 正常
handleStatus(0); // 也正常,但可能不是預期的行為

使用 as const

const Status = {
Success: 0,
Error: 1,
Loading: 2,
} as const;

type Status = (typeof Status)[keyof typeof Status];

function handleStatus(status: Status) {
// ...
}

handleStatus(Status.Success); // 正常
handleStatus(0); // 正常,因為 0 是 Status 的合法值
handleStatus(999); // 錯誤:型別不匹配,TypeScript 會報錯

📋 使用建議

什麼時候用 as const 替代 enum?

  • 當你只需要型別安全:如果你的列舉值只用於型別檢查,as const 是更好的選擇,因為它不會生成運行時程式碼
  • 當你需要更嚴格的型別as const 提供的字面量型別推斷比 enum 更精確
  • 當你需要靈活性as const 可以用在更複雜的物件結構中,而 enum 的語法相對受限

什麼時候保留 enum?

  • 如果你的專案需要運行時的列舉值(例如,與後端 API 交互時需要特定的列舉值),而且你不介意額外的程式碼生成,enum 仍然是一個選項
  • 如果你的團隊已經廣泛使用 enum,且沒有遇到明顯的問題,遷移到 as const 可能需要權衡成本

🎯 總結

enum 在 TypeScript 中有其存在的價值,但在許多情況下,as const 是更現代、更輕量的替代方案。它避免了 enum 的運行時開銷和型別推斷問題,特別適合現代前端專案。

選擇使用哪種方式應該基於你的專案需求:

  • 如果只需要型別安全,選擇 as const
  • 如果需要運行時的列舉值,可以考慮 enum
  • 如果追求最佳效能和型別安全,as const 是更好的選擇

這個話題確實是 TypeScript 開發者值得深入了解的!如果你有具體的程式碼片段或場景想討論,歡迎分享,我可以幫你分析或提供更詳細的建議!