別再寫 TypeScript enum 了!新枚舉方式讓 bundle 瞬間小 20%

image

前幾天排查專案性能問題,發現一個讓人哭笑不得的情況。光是 TypeScriptenum 就占了打包體積的相當一部分!

同事小張一臉無辜地說:“我用 enum 不是為了類型安全嘛...”

沒錯,enum 確實能保證類型安全,但代價有點大了!我嘗試了一種新方法後,打包體積直接減少了 20%,而且類型安全一點都沒少!

為什麼不用 enum?

先看個我們專案裡的真實例子:

// 以前的寫法 - enum
enum UserRole {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
  // ...後面還有十來個角色
}

// 使用的時候
const role = UserRole.Admin

看起來很正常對不對?但你知道編譯成 JavaScript 後發生了什麼嗎?

// 編譯後的代碼
var UserRole;
(function (UserRole) {
    UserRole["Admin"] = "admin";
    UserRole["User"] = "user";
    UserRole["Guest"] = "guest";
    // ...每個值都會生成對應的代碼
})(UserRole || (UserRole = {}));

每個 enum 都會變成一個立即執行函數,專案裡 enum 越多,生成的代碼就越多,打包體積自然就膨脹了。

新的解決方案:常量對象 + 類型別名

來看看我是怎麼改造的:

// 新寫法 - 常量對象
const UserRole = {
  Admin: 'admin',
  User: 'user',
  Guest: 'guest',
} as const  // as const 很重要,讓 TypeScript 知道這是只讀的

// 類型別名 - 相當於枚舉的類型!
type UserRole = keyof typeof UserRole

// 使用時的方式完全一樣!
const role: UserRole = UserRole.Admin

as const 是關鍵!它告訴 TypeScript:“這個對象的值都是固定的,不可動!”

類型別名是什麼意思?

類型別名就是給類型起個新名字,比如這裡的 type UserRole = keyof typeof UserRole,意思是:

  • typeof UserRole:獲取 UserRole 對象的類型
  • keyof:取這個對象的所有鍵(Key)
  • 所以最終 UserRole 類型就是 "Admin" | "User" | "Guest" 這樣的聯合類型

寫代碼會有枚舉一樣的提示嗎?

有的!完全一樣!
當你輸入 UserRole. 的時候,VSCode 還是會自動提示 AdminUserGuest,和用 enum 時一模一樣。

編譯後發生了什麼?

// 編譯後的代碼 - 簡潔多了!
const UserRole = {
    Admin: 'admin',
    User: 'user',
    Guest: 'guest',
};

就這麼簡單! 沒有額外的函數,沒有運行時開銷,就是普通的對象字面量。

實際效果

在我們專案中,替換了所有的 enum
改造前: 打包體積:1.2MB,enum 數量:27 個
改造後: 打包體積:960KB,enum 數量:0 個

體積直接減少了 20%! 而且代碼提示、類型檢查完全沒受影響。

再來看看幾個案例

1. 頁面狀態管理

// 替換前
enum PageStatus {
  Loading = 'loading',
  Success = 'success',
  Error = 'error',
  Empty = 'empty'
}

// 替換後
const PageStatus = {
  Loading: 'loading',
  Success: 'success',
  Error: 'error',
  Empty: 'empty'
} as const
type PageStatus = keyof typeof PageStatus

// 使用體驗完全一致!
function renderPage(status: PageStatus) {
  // ...
}

renderPage(PageStatus.Loading) // ✅ 代碼提示依舊完美

2. 主題配置

// 主題配置
const Theme = {
  Light: 'light',
  Dark: 'dark',
  Auto: 'auto'
} as const
type Theme = keyof typeof Theme

// 使用時照樣有提示
function setTheme(theme: Theme) {
  console.log(`切換至 ${theme} 主題`)
}

setTheme(Theme.Dark) // ✅ 輸入 Theme. 就會自動提示

3. API 錯誤碼

// 錯誤碼定義
const ErrorCode = {
  NotFound: 404,
  Unauthorized: 401,
  ServerError: 500
} as const
type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode]

// 使用
if (error.code === ErrorCode.NotFound) {
  // 處理 404 錯誤
}

需要注意的地方

雖然這個方案很好,但有兩點要注意:

數字枚舉需要稍作調整

const StatusCode = {
  Ok: 200,
  NotFound: 404
} as const
type StatusCode = typeof StatusCode[keyof typeof StatusCode]

如果需要反向映射(根據值找鍵),需要自己寫工具函數

function getKeyByValue(obj: any, value: any) {
  return Object.keys(obj).find(key => obj[key] === value)
}

或者還是直接用枚舉:

enum Status {
  Draft = 1,
  Published = 2
}

const statusName = Status[1] // 返回 "Draft"

總結

簡單來說就是:

  • const obj = { ... } as const 代替 enum
  • type Key = keyof typeof obj 定義類型
  • 打包體積能小很多,構建速度也更快,代碼更簡潔

PS:這個方法需要 TypeScript 3.4+ 版本支持。

覺得有幫助的話,點個收藏分享給更多小夥伴吧~還有什麼問題歡迎留言討論!

我是大華,專注分享前後端開發的實戰筆記。關注我,少走彎路,一起進步!

📌往期精彩

《工作 5 年沒碰過分布式鎖,是我太菜還是公司太穩?網友:太真實了!》
《寫給小公司前端的 UI 規範:別讓頁面醜得自己都看不下去》
《只會寫 Mapper 就敢說會 MyBatis?面試官:原理都沒懂》
《別學 23 種了!Java 專案中最常用的 6 個設計模式,附案例》
《Vue3+TS 設計模式:5 個真實場景讓你代碼更優雅》


原文出處:https://juejin.cn/post/7548763325459611686


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝11   💬6   ❤️9
470
🥈
我愛JS
📝1   💬5   ❤️4
90
🥉
AppleLily
📝1   💬4   ❤️1
51
#4
💬1  
5
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次