前幾天排查專案性能問題,發現一個讓人哭笑不得的情況。光是 TypeScript
的 enum
就占了打包體積的相當一部分!
同事小張一臉無辜地說:“我用 enum
不是為了類型安全嘛...”
沒錯,enum
確實能保證類型安全,但代價有點大了!我嘗試了一種新方法後,打包體積直接減少了 20%,而且類型安全一點都沒少!
先看個我們專案裡的真實例子:
// 以前的寫法 - 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 還是會自動提示 Admin
、User
、Guest
,和用 enum
時一模一樣。
// 編譯後的代碼 - 簡潔多了!
const UserRole = {
Admin: 'admin',
User: 'user',
Guest: 'guest',
};
就這麼簡單! 沒有額外的函數,沒有運行時開銷,就是普通的對象字面量。
在我們專案中,替換了所有的 enum
:
改造前: 打包體積:1.2MB,enum 數量:27 個
改造後: 打包體積:960KB,enum 數量:0 個
體積直接減少了 20%! 而且代碼提示、類型檢查完全沒受影響。
// 替換前
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) // ✅ 代碼提示依舊完美
// 主題配置
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. 就會自動提示
// 錯誤碼定義
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 個真實場景讓你代碼更優雅》