前端"枚举"管理指南

写在前面

郑重承诺以下内容不由 AI 生成

前端里的"枚举",本质上是在解决以下问题:

  • 限制状态范围
  • 提高可读性
  • 提高类型安全
  • 避免魔法字符串
  • 方便 UI 映射
  • 方便 AI 理解

下面作者为同学们总结了前端领域常见的枚举方式:

Typescript 的 enum 枚举类型

enum 是 Typescript 原生支持的枚举类型。许多同学不知道的是,枚举的定义方式是有区别的,定义方式会决定 enum 的应用方式,分为 enum 和 const enum

enum

支持定义数字和字符串,定义/使用方式如下:

ts 复制代码
enum Status {
  Pending,
  Success,
  Failed
}

Status.Pending // 0
ts 复制代码
enum Status {
  Pending = 'pending',
  Success = 'success',
  Failed = 'failed'
}

Status.Pending // pending

数字类型支持反向映射,如:

ts 复制代码
Status[Status.Pending] // Pending

字符串类型不支持反向映射,因为 enum 编译后会输出大致这样的代码:

ts 复制代码
// input
enum Status {
  Pending,
  Success,
  Failed
}

// output
const Status = {
  Pending: 0,
  Success: 1,
  Failed: 2,

  0: "Pending",
  1: "Success",
  2: "Failed"
}

字符串会在设计层面有很多限制,这里随便举个例子:

ts 复制代码
// input
enum Vector {
  X = 'Y',
  Y = 'Z',
  Z = 'X'
}

// output 命名空间已经明显乱掉了, 假设访问 Vector['X'],都不知道访问的是哪一个。
const Vector = {
  X: 'Y',
  Y: 'Z',
  Z: 'X',

  Y: 'X',
  Z: 'Y',
  X: 'Z'
}

const enum 枚举

上文的 enum 是包含 运行时 的,Typescript 会编译出一个 Javascript 对象,但对 bundle 尺寸和性能有要求的库,需要一种更轻的枚举类型,这就是后来 Typescript 团队支持的 const enum 特性。定义/使用方式如下:

ts 复制代码
const enum Status {
  Pending,
  Success,
  Failed
}

Status.Pending // 0

它不支持双向映射,因为 ts 会在编译时将 Status 抹去,只把 Status.Pending 的调用直接替换成字面量 0。

对象枚举

绝大部分的前端工程师的工作还是以业务为主,更多只是单纯的寻求 好用 + 实用 + 类型安全,基本不会在意 bundle 尺寸,实际上对于 Web App 来说,枚举也不会带来明显的 bundle 尺寸增长。 这也催生出了对象枚举的定义方式,优势在于开发者可以自由的定义映射,类型使用也更加明确简单。

ts 复制代码
const Status = {
  Pending: 'pending',
  Success: 'success',
  Failed: 'failed'
} as const 

// as const 是必须的,影响下面的 type Status,对 as const 感兴趣的同学可以去问一下 AI,这里就不展开了。

const StatusLabels = {
  [Status.Pending]: '准备中',
  [Status.Success]: '成功',
  [Status.Failed]: '失败',
}

// 这个类型工具可以提取成通用工具
type ValueOf<T> = T[keyof T]

type Status = ValueOf<typeof Status> // 'pending' | 'success' | 'failed'

Status.Pending // 'pending'
StatusLabels[Status.Pending] // '准备中'

enum-plus

对象枚举虽然一定程度的解决了业务问题,但定义起来实在有点复杂,虽然这对于 AI 来说不是什么难事,但枚举的信息密度非常低,不够内聚,关注点不够聚焦,AI 也经常犯迷糊。 但好在开发社区也有解决方案,比如 enum-plus。

ts 复制代码
import { Enum } from 'enum-plus'

const WeekEnum = Enum({
  Sunday: { value: 0, label: 'I love Sunday' },
  Monday: { value: 1, label: 'I hate Monday' },
});

WeekEnum.Sunday; // 0
WeekEnum.items[0].key; // 'Sunday'
WeekEnum.items[0].label; // 'I love Sunday'

作者找到这个库的时候也是想要直接集成到项目里的,因为它功能非常丰富,提供了 非常非常多 的 api。但同时我也觉得 api 过于复杂了,并且似乎类型约束相对松散,无法直接完成迁移(除非牺牲类型安全)比如:

ts 复制代码
import { Enum } from 'enum-plus'

const WeekEnum = Enum({
  Sunday: { value: 0, label: 'I love Sunday' },
  Monday: { value: 1, label: 'I hate Monday' },
})

console.log(WeekEnum.Sunday)
// 这里不会报类型错误,但 `2` 不是一个合法的枚举值,enum-plus 在这种情况下会返回 undefined,虽然也是合理的,但作者更倾向于严格一些类型约定。
console.log(WeekEnum.label(2))

enumOf

最后是作者自己的版本,个人比较满意。实现较为轻量,api 也比较简单,并且类型安全,国际化支持也非常容易,同时开源并集成到了 rattail 工具库里,有需要的同学可以自取。下面是常用案例。

ts 复制代码
import { enumOf } from 'rattail'

const Status = enumOf({
  Success: { value: 0, label: 'Success' },
  // 字段支持函数返回,国际化支持很简单。
  Warning: { value: 1, label: () => t('global.warning') },
})

Status.Success // 0
Status.label(Status.Success) // 'Success'
Status.options()
/*
[
  { value: 0, label: 'Success'},
  { value: 1, label: 'Warning' },
]
*/
Status.values() // [0, 1]
Status.labels() // ['Success', 'Warning']

你也可以借助 rattail 内置的函数去调整 .options() 返回的数据结构,这个在业务开发里很常见。

ts 复制代码
import { rekey } from 'rattail'

Status.options().map(option => rekey(option, { value: 'key' }))
/*
[
  { key: 0, label: 'Success'},
  { key: 1, label: 'Warning' },
]
*/

你也可以选择扩展更多字段,它们都会有完善的类型推导。

ts 复制代码
const Status = enumOf({
  Success: { value: 1, label: 'Success', color: 'green' },
  Warning: { value: 2, label: 'Warning', color: 'orange' },
})

Status.option(Status.Success).color // 'green'

写在最后

感觉您耐心看完这篇文章,希望您能喜欢。这里是《前端毕业班》,前端开发者的自救互助小组。在 AI 与不确定性并存的时代,我们一起看清焦虑,聊技术、聊趋势,也聊前端还能走多远,走去哪。

相关推荐
门思科技13 小时前
LoRaWAN 网络排障的五层漏斗:从"上不到云"到"数值不对",每一层该用什么工具证明
前端
计算机魔术师13 小时前
Cursor 官方插件商城现恶意 Claude Code 扩展,约 11 小时影响最多 5000 名开发者
前端
掘金者阿豪13 小时前
MongoDB迁移到金仓数据库:商品属性继续灵活存,订单报表交给 SQL
前端·后端
栉甜13 小时前
Vue2学习内容(上)
前端·javascript·vue.js·学习
梦曦i14 小时前
v0.2.0 架构升级:class + reactive 重塑状态管理
前端·uni-app x
布丁爱干饭14 小时前
寄给午后
前端
java1234_小锋14 小时前
【技术专题】CSS3 网页技术 - CSS3 布局进阶
前端·css·css3
zhippt_101414 小时前
只剩截图没有源文件?图转PPT把图片还原成可编辑幻灯片
前端·人工智能·powerpoint
shmily麻瓜小菜鸡15 小时前
TypeScript 知识点补充(第三部分:工程化与高级类型)
前端·javascript·typescript
10岁的博客15 小时前
Chrome 扩展安全攻防实录:从权限模型到真实漏洞复盘
前端·chrome·安全