介绍

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

业务需求

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

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

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

原始代码

前端数据类型定义, 首先定义一个字面量用来保存Job状态,然后定义一个Job接口来描述Job对象。

// 定义Job状态字面量
type JobStatus = 'PENDING' | 'RUNNING' | 'SUCCESS' | 'FAILED' | 'CANCELED'; // 每个Job包含id、name和status三个属性
interface Job {
id: string;
name: string;
status: JobStatus;
}

后端返回数据如下,可以看到后端是用数字类型来表示状态的。

const jobData = [
{ id: '1', name: 'Job 1', status: 1 }, // PENDING
{ id: '2', name: 'Job 2', status: 2 }, // RUNNING
{ id: '3', name: 'Job 3', status: 3 }, // SUCCESS
{ id: '4', name: 'Job 4', status: 4 }, // FAILED
{ id: '5', name: 'Job 5', status: 5 }, // CANCELED
];

为了将后端返回的数字类型和前端定义的Job Status对应起来,我又额外写了一个映射函数:

function mapJobStatus(status: number): JobStatus {
switch (status) {
case 1:
return 'PENDING';
case 2:
return 'RUNNING';
case 3:
return 'SUCCESS';
case 4:
return 'FAILED';
case 5:
return 'CANCELED';
default:
throw new Error(`Unknown status: ${status}`);
}
}

接下来就是展示了,展示Job状态时,用户不想看到全大写的状态,而是想看到首字母大写的状态,所以我又写了一个函数来处理这个问题:

function getJobDisplayName(status: JobStatus): string {
return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
} /* 转换后的状态字符串如下:
PENDING -> Pending
RUNNING -> Running
SUCCESS -> Success
FAILED -> Failed
CANCELED -> Canceled
*/

好了,下面我们停下来思考一下,以上这些代码都解决了哪些问题,为什么需要两个转换函数,有没有更好的解决方式?

问题分析

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

  1. 后端状态码到前端状态的转换(1,2,3,4,5 -> PENDING, RUNNING, SUCCESS, FAILED, CANCELED)
  2. 前端状态字面量到展示字符串的转换(PENDING, RUNNING, SUCCESS, FAILED, CANCELED -> Pending, Running, Success, Failed, Canceled)

对于第一点,可以使用枚举类型来实现,这样就不需要手动维护状态码和状态字面量之间的映射关系了。

对于第二点,原本的实现是将全大写的状态转换为首字母大写的形式,这种转换方式比较简单,但实际业务中,可能会有更复杂的需求,比如用户希望看到不同的展示字符串(例如将RUNNING显示为In progress)。因此,使用一个映射表来处理这种转换会更加灵活。

优化后的代码

我们可以使用 TypeScript 的枚举类型来简化代码。首先定义一个枚举来表示 Job 状态:

enum JobStatus {
PENDING = 1,
RUNNING = 2,
SUCCESS = 3,
FAILED = 4,
CANCELED = 5,
}

这样就可以省去第一个转换函数mapJobStatus,因为枚举本身就提供了状态码到状态字面量的映射,可以直接使用这个枚举来定义 Job 接口:

interface Job {
id: string;
name: string;
status: JobStatus; // 使用枚举类型
}

接下来,重写getJobDisplayName, 这里使用typescript的Record类型来创建一个映射表(Record类型相当于一个键值对的映射,只不过键和值都是类型化的),将枚举值映射到展示字符串,与原本的实现方式相比,这种方式更加简洁易维护。

const getJobDisplayName: Record<JobStatus, string> = {
[JobStatus.PENDING]: 'Pending',
[JobStatus.RUNNING]: 'In progress',
[JobStatus.SUCCESS]: 'Success',
[JobStatus.FAILED]: 'Failed',
[JobStatus.CANCELED]: 'Canceled',
};

最后是调用代码,如下:

const jobs = [
{ id: '1', name: 'Job 1', status: 1 },
{ id: '2', name: 'Job 2', status: 2 },
{ id: '3', name: 'Job 3', status: 3 },
{ id: '4', name: 'Job 4', status: 4 },
{ id: '5', name: 'Job 5', status: 5 },
]; jobs.forEach((job) => {
console.log(
`Job ID: ${job.id}, Name: ${job.name}, Status: ${
getJobDisplayName[job.status as JobStatus]
}`
);
});

使用枚举类型的好处是:

  1. 可读性:枚举提供了更清晰的语义,
  2. 类型安全:TypeScript 的枚举类型可以确保状态值的合法性,避免了手动维护映射关系的错误。
  3. 简化代码:减少了转换函数的数量,使代码更简洁
  4. 易于维护:如果需要添加新的状态,只需在枚举中添加即可,不需要修改多个地方。

有没有更好的实现方式?很想听听大家的想法,欢迎留言讨论。

今天就到这里了,我们明天见。

TypeScript枚举类型应用:前后端状态码映射的最简方案的更多相关文章

  1. typescript枚举,类型推论,类型兼容性,高级类型,Symbols(学习笔记非干货)

    枚举部分 Enumeration part 使用枚举我们可以定义一些有名字的数字常量. 枚举通过 enum关键字来定义. Using enumerations, we can define some ...

  2. TypeScript——枚举类型

    enum类型是对JavaScript标准数据类型的一个补充. 在运行环境下编译成对象, 可用属性名索引, 也可用属性值索引.而其实现原理为:反向映射 (如下例)   数字枚举 enum Role { ...

  3. django开发最完美手机购物商城APP带前后端源码

    后端和数据接口,全采用django开发 从0到大神的进阶之路 一句话,放弃单文件引用vue.js练手的学习方式 马上从vue-cli4练手,要不然,学几年,你也不懂组件式开发,不懂VUEX,不懂路由, ...

  4. 微信公众号商城、小程序商城、H5商城 实例 前后端源码

    CRMEB客户管理+电商营销系统  https://gitee.com/ZhongBangKeJi/CRMEB 演示站后台: http://demo.crmeb.net/admin 账号:demo 密 ...

  5. 【vue3-element-admin 】基于 Vue3 + Vite4 + TypeScript + Element-Plus 从0到1搭建后台管理系统(前后端开源@有来开源组织)

    vue3-element-admin 是基于 vue-element-admin 升级的 Vue3 + Element Plus 版本的后台管理前端解决方案,技术栈为 Vue3 + Vite4 + T ...

  6. TypeScript 中枚举类型的理解?应用场景?

    一.是什么 枚举是一个被命名的整型常数的集合,用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型 通俗来说,枚举就是一个对象的所有可能取值的集合 在日常生活中也很常见,例如表 ...

  7. .net工具类 获取枚举类型的描述

    一般情况我们会用枚举类型来存储一些状态信息,而这些信息有时候需要在前端展示,所以需要展示中文注释描述. 为了方便获取这些信息,就封装了一个枚举扩展类. /// <summary> /// ...

  8. 前后端分离djangorestframework——restful规范

    restful现在非常流行,所以很有必要提一下 web服务交互 在浏览器中能看到的每个网站,都是一个web服务.那么我们在提供每个web服务的时候,都需要前后端交互,前后端交互就一定有一些实现方案,我 ...

  9. 使用 Nginx 部署前后端分离项目,解决跨域问题

    前后端分离这个问题其实松哥和大家聊过很多了,上周松哥把自己的两个开源项目部署在服务器上以帮助大家可以快速在线预览(喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了 ...

  10. http 状态码含义

    HTTP状态码被分为五大类, 目前我们使用的HTTP协议版本是1.1, 支持以下的状态码.随着协议的发展,HTTP规范中会定义更多的状态码. 小技巧: 假如你看到一个状态码518, 你并不知道具体51 ...

随机推荐

  1. 使用Python可视化潮汐力

    引言 潮汐力,简单来说,就是天体间由于引力差异而产生的力量.这种力在地球上最显著的表现就是潮汐现象,即海水的涨落.潮汐力是由月球和太阳的引力作用引起的,它对地球的影响非常深远,除了海洋潮汐外,还能影响 ...

  2. AoP的相关术语

    一.Joinpoint(连接点): 所谓连接点是指那些被拦截到的点.在 spring 中,这些所谓的点指就是方法,因为 spring 只支持方法类型的连接点. 二.Pointcut(切入点): 所谓切 ...

  3. JAVA基础之多线程一期

    一.并发与并行的区别 并发:指同一时间段,两个或多个事件交替进行 并行:指同一时间段,两个或多个事件同时进行 二.进程与线程的区别 进程:正在内存中运行的程序就是进程 线程:线程归属于进程,它是进程中 ...

  4. kettle安装文件下载(含多版本)

    kettle是一款基于java开发的洗数工具,可以通过图像化的操作界面,拖拉拽的操作方式,实现数据导入导出清洗等功能,还支持编写脚本进行数据处理,功能十分强大. 本文主要记录一下kettle各版本下载 ...

  5. AD 侦查-MSRPC Over SMB

    本文通过 Google 翻译 AD Recon – MSRPC Over SMB (135/139/445) 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充. 导航 0 ...

  6. Faray 数列问题

    首先,Farey 数列 \(F_n\) 表示分母不超过 \(n\) 的所有既约真分数按大小顺序排列的集合,形式化来说 \[F_n = \left\{\frac{p}{q} \bigg\vert 0 & ...

  7. 为Feign客户端自定义ErrorDecoder

    摘要:重写Feign的错误解码器ErrorDecoder,以自定义业务逻辑.   ErrorDecoder,顾名思义,它是发生错误或者异常情况时使用的一种解码器,允许我们对异常进行特殊处理.   在配 ...

  8. git提示:There is no tracking information for the current branch

    问题 使用git pull拉取远程分支代码的时候,提示: > There is no tracking information for the current branch. Please sp ...

  9. MyBatis常见面试题:通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

      MyBatis常见面试题:通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?   Dao接口即Mapper接 ...

  10. Java虚拟机之垃圾回收器

      上面有7类垃圾回收器,分为两块,上面为新生代(Young generation)回收器,下面是老年代(Tenured generation)回收器.如果两个回收器之间存在连线,就说明它们可以搭配使 ...