找回密码
 立即注册
首页 业界区 业界 TypeScript枚举类型应用:前后端状态码映射的最简方案 ...

TypeScript枚举类型应用:前后端状态码映射的最简方案

山芷兰 2025-7-13 09:10:56
介绍

这篇文章来谈一下 TypeScript 中的枚举类型(Enum)以及一些最佳实践。事情的起因是这样的,今天看到自己之前写的一段代码,感觉不是很好,于是想优化一下,期间用到了枚举类型,遂记录一下。为了方便理解,我将原来的例子简化一下。
业务需求

业务需求是这样的:我们要实现一个Job系统,你可以将其想象为Jenkins Job类似的东西,每个Job有一个状态,状态可以是以下几种:

  • PENDING:等待执行
  • RUNNING:正在执行
  • SUCCESS:执行成功
  • FAILED:执行失败
  • CANCELED:执行被取消

Job的状态信息由后端返回,前端只负责展示,也不需要实时刷新。很简单的需求,对吧?我的原始代码如下:
原始代码

前端数据类型定义, 首先定义一个字面量用来保存Job状态,然后定义一个Job接口来描述Job对象。
  1. // 定义Job状态字面量
  2. type JobStatus = 'PENDING' | 'RUNNING' | 'SUCCESS' | 'FAILED' | 'CANCELED';
  3. // 每个Job包含id、name和status三个属性
  4. interface Job {
  5.   id: string;
  6.   name: string;
  7.   status: JobStatus;
  8. }
复制代码
后端返回数据如下,可以看到后端是用数字类型来表示状态的。
  1. const jobData = [
  2.   { id: '1', name: 'Job 1', status: 1 }, // PENDING
  3.   { id: '2', name: 'Job 2', status: 2 }, // RUNNING
  4.   { id: '3', name: 'Job 3', status: 3 }, // SUCCESS
  5.   { id: '4', name: 'Job 4', status: 4 }, // FAILED
  6.   { id: '5', name: 'Job 5', status: 5 }, // CANCELED
  7. ];
复制代码
为了将后端返回的数字类型和前端定义的Job Status对应起来,我又额外写了一个映射函数:
  1. function mapJobStatus(status: number): JobStatus {
  2.   switch (status) {
  3.     case 1:
  4.       return 'PENDING';
  5.     case 2:
  6.       return 'RUNNING';
  7.     case 3:
  8.       return 'SUCCESS';
  9.     case 4:
  10.       return 'FAILED';
  11.     case 5:
  12.       return 'CANCELED';
  13.     default:
  14.       throw new Error(`Unknown status: ${status}`);
  15.   }
  16. }
复制代码
接下来就是展示了,展示Job状态时,用户不想看到全大写的状态,而是想看到首字母大写的状态,所以我又写了一个函数来处理这个问题:
  1. function getJobDisplayName(status: JobStatus): string {
  2.   return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
  3. }
  4. /* 转换后的状态字符串如下:
  5. PENDING -> Pending
  6. RUNNING -> Running
  7. SUCCESS -> Success
  8. FAILED -> Failed
  9. CANCELED -> Canceled
  10. */
复制代码
好了,下面我们停下来思考一下,以上这些代码都解决了哪些问题,为什么需要两个转换函数,有没有更好的解决方式?
问题分析

为了完成这个需求,上述代码做了以下几件事:

  • 后端状态码到前端状态的转换(1,2,3,4,5 -> PENDING, RUNNING, SUCCESS, FAILED, CANCELED)
  • 前端状态字面量到展示字符串的转换(PENDING, RUNNING, SUCCESS, FAILED, CANCELED -> Pending, Running, Success, Failed, Canceled)
对于第一点,可以使用枚举类型来实现,这样就不需要手动维护状态码和状态字面量之间的映射关系了。
对于第二点,原本的实现是将全大写的状态转换为首字母大写的形式,这种转换方式比较简单,但实际业务中,可能会有更复杂的需求,比如用户希望看到不同的展示字符串(例如将RUNNING显示为In progress)。因此,使用一个映射表来处理这种转换会更加灵活。
优化后的代码

我们可以使用 TypeScript 的枚举类型来简化代码。首先定义一个枚举来表示 Job 状态:
  1. enum JobStatus {
  2.   PENDING = 1,
  3.   RUNNING = 2,
  4.   SUCCESS = 3,
  5.   FAILED = 4,
  6.   CANCELED = 5,
  7. }
复制代码
这样就可以省去第一个转换函数mapJobStatus,因为枚举本身就提供了状态码到状态字面量的映射,可以直接使用这个枚举来定义 Job 接口:
  1. interface Job {
  2.   id: string;
  3.   name: string;
  4.   status: JobStatus; // 使用枚举类型
  5. }
复制代码
接下来,重写getJobDisplayName, 这里使用typescript的Record类型来创建一个映射表(Record类型相当于一个键值对的映射,只不过键和值都是类型化的),将枚举值映射到展示字符串,与原本的实现方式相比,这种方式更加简洁易维护。
  1. const getJobDisplayName: Record<JobStatus, string> = {
  2.   [JobStatus.PENDING]: 'Pending',
  3.   [JobStatus.RUNNING]: 'In progress',
  4.   [JobStatus.SUCCESS]: 'Success',
  5.   [JobStatus.FAILED]: 'Failed',
  6.   [JobStatus.CANCELED]: 'Canceled',
  7. };
复制代码
最后是调用代码,如下:
  1. const jobs = [
  2.   { id: '1', name: 'Job 1', status: 1 },
  3.   { id: '2', name: 'Job 2', status: 2 },
  4.   { id: '3', name: 'Job 3', status: 3 },
  5.   { id: '4', name: 'Job 4', status: 4 },
  6.   { id: '5', name: 'Job 5', status: 5 },
  7. ];
  8. jobs.forEach((job) => {
  9.   console.log(
  10.     `Job ID: ${job.id}, Name: ${job.name}, Status: ${
  11.       getJobDisplayName[job.status as JobStatus]
  12.     }`
  13.   );
  14. });
复制代码
使用枚举类型的好处是:

  • 可读性:枚举提供了更清晰的语义,
  • 类型安全:TypeScript 的枚举类型可以确保状态值的合法性,避免了手动维护映射关系的错误。
  • 简化代码:减少了转换函数的数量,使代码更简洁
  • 易于维护:如果需要添加新的状态,只需在枚举中添加即可,不需要修改多个地方。
有没有更好的实现方式?很想听听大家的想法,欢迎留言讨论。
今天就到这里了,我们明天见。
出处:http://www.cnblogs.com/graphics/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册