一般 ts 项目不推荐使用 enum 的原因
示例代码
export const IntroduceShopSteps = {
CUSTOMIZE_SHOP: 'customize_shop',
ADD_MODULE: 'add_module',
PREVIEW_SHOP: 'preview_shop',
INTRODUCE_CONTENT_TABS: 'introduce_content_tabs',
} as const;
export type IntroduceShopSteps = (typeof IntroduceShopSteps)[keyof typeof IntroduceShopSteps];
export const IntroduceShopSteps = {
CUSTOMIZE_SHOP: 'customize_shop',
ADD_MODULE: 'add_module',
PREVIEW_SHOP: 'preview_shop',
INTRODUCE_CONTENT_TABS: 'introduce_content_tabs',
} as const;
export type IntroduceShopSteps = (typeof IntroduceShopSteps)[keyof typeof IntroduceShopSteps];
你提供的写法(使用 as const 的对象字面量 + 类型提取)相比 TypeScript 的 enum 有以下几个主要优点:
✅ 1. 更符合 JavaScript 原生习惯,无运行时开销
enum在编译后会生成额外的 JavaScript 代码(双向映射),增加 bundle 体积。- 而
as const对象是纯字面量,在运行时就是普通对象,零额外开销,尤其适合前端或对包大小敏感的场景。
// enum 编译后(反向映射)
var IntroduceShopSteps;
(function (IntroduceShopSteps) {
IntroduceShopSteps["CUSTOMIZE_SHOP"] = "customize_shop";
// ...
})(IntroduceShopSteps || (IntroduceShopSteps = {}));
而你的写法编译后就是:
export const IntroduceShopSteps = {
CUSTOMIZE_SHOP: 'customize_shop',
// ...
};
✅ 2. 字符串字面量类型更精确
- 使用
as const后,每个值都被推断为具体的字符串字面量类型(如'customize_shop'),而不是宽泛的string。 - 配合
type IntroduceShopSteps = (typeof ...)[keyof ...],你得到的是一个联合类型:
type IntroduceShopSteps = 'customize_shop' | 'add_module' | 'preview_shop' | 'introduce_content_tabs';
这比 enum 的成员类型(虽然也是字面量)在某些高级类型操作中更灵活、更“透明”。
✅ 3. 更好的 Tree-shaking 支持
- 因为它是普通对象导出,现代打包工具(如 Vite、Webpack、Rollup)可以轻松进行 tree-shaking。
- 而
enum是一个封闭作用域内的变量声明,难以被 tree-shaken,即使你只用其中一个值,整个 enum 可能都会被打包进去。
✅ 4. 避免 enum 的陷阱
enum允许动态访问(如IntroduceShopSteps[someString]),可能返回undefined或意外值,破坏类型安全。- 而
as const对象配合类型系统,强制你在使用前做合法性检查(除非显式放宽类型)。
✅ 5. 与现代 TS/JS 生态更契合
- 许多库(如 React、Redux Toolkit、Zod 等)都采用这种“const object + union type”模式。
- 它天然支持 ESM 按需导入、模块解析清晰,也更容易与 JSON Schema、OpenAPI 等外部系统对接(因为值就是字符串)。
⚠️ 小缺点(权衡)
- 不能像
enum那样直接用IntroduceShopSteps.CUSTOMIZE_SHOP作为类型(但你可以用typeof IntroduceShopSteps.CUSTOMIZE_SHOP)。 - 书写略冗长(需要手动写类型提取那一行),但可通过工具或模板缓解。
总结
| 特性 | as const 对象 | enum |
|------|------------------|--------|
| 运行时体积 | 极小(原生对象) | 较大(双向映射) |
| 类型精度 | 高(字面量联合) | 高 |
| Tree-shaking | ✅ 支持 | ❌ 难以支持 |
| 可读性/习惯 | 更贴近 JS | TS 特有语法 |
| 安全性 | 更严格(无隐式动态访问) | 可能意外访问无效 key |
因此,在大多数现代 TypeScript 项目中(尤其是前端),推荐使用
as const+ 类型提取的方式替代 string enum。这也是社区(如 ESLint、React 团队)逐渐推崇的做法。
如果你追求极致的类型安全、包体积和可维护性,你的写法是更优选择。