TypeScript 官方宣布弃用 Enum?Enum 何罪之有?

1. 官方真的不推荐 Enum 了吗?

1.1 事情的起因

起因是看到 科技爱好者周刊(第 340 期) 里面推荐了一篇文章,说是官方不再推荐使用 enum 语法,原文链接在 An ode to TypeScript enums,大致是说 TS 新出了一个 --erasableSyntaxOnly 配置,只允许使用可擦除语法,但是 enum 不可擦除,因此推断官方已不再推荐使用 enum 了。官方并没有直接表达不推荐使用,那官方为什么要出这个配置项呢?

1.2 什么是可擦除语法

就在上周,TypeScript 发布了 5.8 版本,其中有一个改动是添加了 --erasableSyntaxOnly 配置选项,开启后仅允许使用可擦除语法,否则会报错enum 就是一个不可擦除语法,开启 erasableSyntaxOnly 配置后,使用 enum 会报错。

例如,如果在 tsconfig 文件中配置 "erasableSyntaxOnly": true(只允许可擦除语法),此时使用不可擦除语法,将会得到报错:

可擦除语法就是可以直接去掉的、仅在编译时存在、不会生成额外运行时代码的语法,例如 type。不可擦除语法就是不能直接去掉的、需要编译为JS且会生成额外运行时代码的语法,例如 enum 具体举例如下:

可擦除语法,不生成额外运行时代码,比如 typelet n: numberinterfaceas number 等:

不可擦除语法,生成额外运行时代码,比如 enumnamespace、类属性参数构造语法糖(Class Parameter properties)等:

ts 复制代码
// 枚举类型
enum METHOD {
  ADD = 'add'
}

// 类属性参数构造
class A {
  constructor(public x: number) {}
}
let a: number = 1
console.log(a)

1.3 TS 官方为什么要出 erasableSyntaxOnly?

官方既然没有直接表达不推荐 enum,那为什么要出 erasableSyntaxOnly 配置来排除 enum 呢?

我找到了 TS 官方文档(The --erasableSyntaxOnly Option)说明:

大致意思是说之前 Node 新版本中支持了执行 TS 代码的能力,可以直接运行包含可擦除语法的 TypeScript 文件。Node 将用空格替换 TypeScript 语法,并且不执行类型检查。总结下来就是:

在 Node 22 版本:

  • 需要配置 --experimental-transform-types 执行支持 TS 文件
  • 要禁用 Node 这种特性,使用参数 --no-experimental-strip-types

在 Node 23.6.0 版本:

  • 默认支持直接运行可擦除语法 的 TS 文件,删除参数 --no-experimental-strip-types
  • 对于不可擦除语法 ,使用参数 --experimental-transform-types

综上所述,TS 官方为了配合 Node.js 这次改动(即默认允许直接执行不可擦除语法的 TS 代码),才添加了一个配置项 erasableSyntaxOnly,只允许可擦除语法。

2. Enum 的三大罪行

2.1 枚举默认值

自 enum 从诞生以来,它一直是前端界最具争议的特性之一,许多前端开发者乃至不少大佬都对其颇有微词,纷纷发起了 DO NOT USE TypeScript Enum 的吐槽。那么enum 真的有那么难用吗?

enum 默认的枚举值从 0 开始,这还不是最关键的,你传入了默认枚举值时,居然是合法的,这无形之中带来了类型安全问题。

ts 复制代码
enum METHOD {
    ADD
}

function doAction(method: METHOD) {
  // some code
}

doAction(METHOD.ADD) // ✅ 可以
doAction(0) // ✅ 可以

2.2 不支持枚举值字面量

还有一种场景,我要求既可以传入枚举类型,又要求传入枚举值字面量,如下所示,但是他又不合法了?(有人说你定义传枚举类型就要传相应的枚举,这没问题,但是上面提到的问题又是怎么回事呢?这何尝不是 Enum 的双标?)

ts 复制代码
enum METHOD {
    ADD = 'add'
}

function doAction(method: METHOD) {
  // some code
}

doAction(METHOD.ADD) // ✅ 可以
doAction('add') // ❌ 不行	

2.3 增加运行时开销

TypeScript 的 enum 在编译后会生成额外的 JavaScript 双向映射数据,这会增加运行时的开销。

3. Enum 的替代方案

众所周知,TS 一大特性是类型变换,我们可以通过类型操作组合不同类型来达到目标类型,又称为类型体操。下面的四种解决方案,可以根据实际需求来选择。

3.1 const enum

const enum 是解决产生额外生成的代码和额外的间接成本有效且快捷的方法,也是官方推荐的。

ts 复制代码
const enum METHOD {
  ADD = 'add',
  DELETE = 'delete',
  UPDATE = 'update',
  QUERY = 'query',
}

function doAction(method: METHOD) {
    // some code
}

doAction(METHOD.ADD) // ✅ 可行
doAction('delete') // ❌ 不行

const enum 解析后的代码中引用 enum 的地方将直接被替换为对应的枚举值:

3.2 模板字面量类型

将枚举类型包装为模板字面量类型(Template Literal Types),从而即支持枚举类型,又支持枚举值字面量,但是没有解决运行时开销问题。

ts 复制代码
enum METHOD {
  ADD = 'add',
  DELETE = 'delete',
  UPDATE = 'update',
  QUERY = 'query',
}

type METHOD_STRING = `${METHOD}`

function doAction(method: METHOD_STRING) {
    // some code
}

doAction(METHOD.ADD) // ✅ 可行
doAction('delete') // ✅ 可行
doAction('remove') // ❌ 不行

3.3 联合类型(Union Types)

使用联合类型,引用时可匹配的值限定为指定的枚举值了,但同时也没有一个地方可以统一维护枚举值,如果一旦枚举值有调整,其他地方都需要改。

ts 复制代码
type METHOD =
  | 'add'
  /**
   * @deprecated 不再支持删除
   */
  | 'delete'
  | 'update'
  | 'query'


function doAction(method: METHOD) {
    // some code
}

doAction('delete') // ✅ 可行,没有 TSDoc 提示
doAction('remove') // ❌ 不行

3.4 类型字面量 + as const

类型字面量就是一个对象,将一个对象断言(Type Assertion)为一个 const,此时这个对象的类型就是对象字面量类型,然后通过类型变换,达到即可以传入枚举值,又可以传入枚举类型的目的。

ts 复制代码
const METHOD = {
  ADD:'add',
  /**
  * @deprecated 不再支持删除
  */
  DELETE:'delete',
  UPDATE: 'update',
  QUERY: 'query'
} as const

type METHOD_TYPE = typeof METHOD[keyof typeof METHOD]

function doAction(method: METHOD_TYPE) {
  // some code
}

doAction(METHOD.DELETE) // ✅ 可行,没有 TSDoc 提示
doAction('delete') // ✅ 可行
doAction('remove') // ❌ 不行

4. 总结

  • TS 可擦除语法 是指 typeinterfacen:number 等可以直接去掉的、仅在编译时存在、不会生成额外运行时代码的语法
  • TS 不可擦除语法 是指 enumconstructor(public x: number) {} 等不可直接去除且会生成额外运行时代码的语法
  • Node.js 23.6.0 版本开始 默认支持直接执行可擦除语法 的 TS 文件
  • enum 的替代方案有多种,取决于实际需求。用字面量类型 + as const 是比较常用的一种方案。

TS 官方为了兼容 Node.js 23.6.0 这种可执行 TS 文件特性,出了 erasableSyntaxOnly 配置禁用不可擦除语法,反映了 TypeScript 官方对减少运行时开销和优化编译输出的关注,而不是要放弃 enum

但或许未来就是要朝着这个方向把 enum 优化掉也说不定呢?

5. 参考链接

相关推荐
Joeysoda15 分钟前
JavaEE进阶(2) Spring Web MVC: Session 和 Cookie
java·前端·网络·spring·java-ee
小周同学:1 小时前
npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本。
前端·npm·node.js
浪遏1 小时前
面试官:字符串反转有多少种实现方式 ?| 一道题目检测你的基础
前端·面试
kjl5365661 小时前
前端题目类型
前端
IT、木易1 小时前
大白话CSS 优先级计算规则的详细推导与示例
前端·css·面试
TinTin Land2 小时前
后 Safe 时代:多签钱包安全新范式与防范前端攻击的新思路
前端·安全
IT、木易2 小时前
大白话html语义化标签优势与应用场景
前端·html
知识分享小能手2 小时前
Html5学习教程,从入门到精通, HTML5 新的 Input 类型:语法知识点与案例代码(16)
开发语言·前端·学习·html·html5·web·java开发
歡進2 小时前
开源 | Warpvas 实现扭曲的画布
javascript·webgl·canvas
Georgewu2 小时前
【HarmonyOS Next】鸿蒙应用故障处理思路详解
前端·vue.js·harmonyos