别再写 TypeScript enum了!新枚举方式让 bundle 瞬间小20%

前几天排查项目性能问题,发现一个让人哭笑不得的情况。光是TypeScriptenum就占了打包体积的相当一部分!

同事小张一脸无辜地说:"我用enum不是为了类型安全嘛..."

没错,enum确实能保证类型安全,但代价有点大了!我尝试了一种新方法后,打包体积直接减少了20%,而且类型安全一点都没少!

为什么不用enum?

先看个我们项目里的真实例子:

typescript 复制代码
// 以前的写法 - enum
enum UserRole {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
  // ...后面还有十来个角色
}

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

看起来很正常对不对?但你知道编译成JavaScript后发生了什么吗?

javascript 复制代码
// 编译后的代码
var UserRole;
(function (UserRole) {
    UserRole["Admin"] = "admin";
    UserRole["User"] = "user";
    UserRole["Guest"] = "guest";
    // ...每个值都会生成对应的代码
})(UserRole || (UserRole = {}));

每个enum都会变成一个立即执行函数,项目里enum越多,生成的代码就越多,打包体积自然就膨胀了。

新的解决方案:常量对象 + 类型别名

来看看我是怎么改造的:

typescript 复制代码
// 新写法 - 常量对象
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时一模一样。

编译后发生了什么?

javascript 复制代码
// 编译后的代码 - 简洁多了!
const UserRole = {
    Admin: 'admin',
    User: 'user',
    Guest: 'guest',
}

就这么简单! 没有额外的函数,没有运行时开销,就是普通的对象字面量。

实际效果

在我们项目中,替换了所有的enum

改造前: 打包体积:1.2MB,enum数量:27个 改造后: 打包体积:960KB,enum数量:0个

体积直接减少了20%! 而且代码提示、类型检查完全没受影响。

再来看看几个案例

1. 页面状态管理

typescript 复制代码
// 替换前
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. 主题配置

typescript 复制代码
// 主题配置
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错误码

typescript 复制代码
// 错误码定义
const ErrorCode = {
  NotFound: 404,
  Unauthorized: 401,
  ServerError: 500
} as const
type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode]

// 使用
if (error.code === ErrorCode.NotFound) {
  // 处理404错误
}

需要注意的地方

虽然这个方案很好,但有两点要注意:

数字枚举需要稍作调整

typescript 复制代码
const StatusCode = {
  Ok: 200,
  NotFound: 404
} as const
type StatusCode = typeof StatusCode[keyof typeof StatusCode]

如果需要反向映射(根据值找键),需要自己写工具函数

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

或者还是直接用枚举

typescript 复制代码
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个真实场景让你代码更优雅》

相关推荐
dsyyyyy11011 小时前
JavaScript变量
开发语言·javascript·ecmascript
kyriewen2 小时前
手写 Promise.all、race、any:不到 30 行代码,解决并发异步的所有姿势
前端·javascript·面试
胡志辉的博客3 小时前
深入浅出理解浏览器事件循环:从一道输出题讲到 Chrome 源码
前端·javascript·chrome·chromium·event loop
代码不加糖3 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
退休倒计时3 小时前
【每日一题】LeetCode 53. 最大子数组和 TypeScript
数据结构·算法·leetcode·typescript
懂懂tty3 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js
小二·4 小时前
Next.js 15 全栈开发实战
开发语言·javascript·ecmascript
Rain5095 小时前
2.1 Nest.js 项目初始化与模块化架构
开发语言·前端·javascript·后端·架构·数据分析·node.js
拾年2756 小时前
从零手写 Ajax:用原生 XHR 搭建前后端交互全流程
前端·javascript·ajax
拉勾科研工作室6 小时前
区块链工程毕业论文题目【249个】
开发语言·javascript