用 Enum 提升TypeScript代码的可读性

Enum

Enum 是在 TypeScript 中新增的语法,也叫作枚举,通常用它来管理多个相同系列的常量(即不能被修改的变量),用于状态的判断。javascript

在 Web 中比较常见的状态判断,是在处理请求时,要针对不一样的响应状态码作对应的处理:前端

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case 200: // 请求成功时
      // Do something...
      break;
    case 400: // 请求失败时
      // Do something...
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

但由于响应状态码都是预先定义好的,因此没什么争议,代码写成这样看也很正常,可是若是后端在服务器发生错误时自定义了一些编码,并告诉前端,这些代码都表明什么错误,那么上面的函数可能会变成这样:java

const handleWrongStatus = (status: string): void => {
  switch (status) {
    case 'A':
      // Do something...
      break;
    case 'B':
      // Do something...
      break;
    case 'C':
      // Do something...
      break;
    default:
      throw (new Error('No have wrong code!'));
  }
};

若是是这种代码,别说是刚接手的人,就算是你本身两星期前写的,恐怕不去翻文档也想不起它们都表明什么了吧。程序员

可是若是善用 Enum ,就能够避免上述发生的状况。面试

基本用法

先来看看 Enum 该怎么定义,它和 Object 的用法很像:typescript

enum requestStatusCodes {
  error,
  success,
}

不须要在内容与名称之间加等号,直接在大括号内叙述该 Enum 中具备哪些变量,与其说是变量,不如说是常量更恰当些,由于在 Enum 中的值是不可修改的,因此也没必要担忧这些定义好的规则会在代码执行的过程当中发生改变,致使执行错误。segmentfault

而既然 Enum 是用来定义同一个系列常量的,那这些常量应该都能维护特定的值。没错,在 Enum 中的每一个常量,均可以经过 = 来指定具体的值 。后端

但若是是像前面的 requestStatusCodes ,没有为 errorsuccess 指定具体的值也不会出错,由于 TypeScript 会从 0 开始自动递增定义值,因此签名的 requestStatusCodes 会和下面的结果相同:服务器

enum requestStatusCodes {
  error = 0,
  success = 1,
}console.log(requestStatusCodes.error) // 0
console.log(requestStatusCodes.success) // 1

除了数字外,也能够定义为字串:微信

enum requestWrongCodes {
  missingParameter = 'A',
  wrongParameter = 'B',
  invalidToken = 'C',
}console.log(requestWrongCodes.wrongParameter) // 'B'

固然也能够在一个 enum 中设定不一样的类型,但这样一点意义都没有:

enum requestStatusCodes {
  error = 0,
  success = 'OK',
}

了解基本的 Enum 怎么定义后,接着就来改写前面代码中的 handleResponseStatushandleWrongStatus ,让它们在语义上可以更明确。

首先用 Enum 定义二者的状态描述:

enum requestStatusCodes {
  error = 400,
  success = 200,
}

enum requestWrongCodes {
  missingParameter = 'A',
  wrongParameterType = 'B',
  invalidToken = 'C',
}

而后修改 handleResponseStatushandleWrongStatus 中的 Switch 判断:

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case requestStatusCodes.success:
      // Do something...
      break;
    case requestStatusCodes.error:
      // Do something...
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

const handleWrongStatus = (status: string): void => {
  // 若是以为 requestWrongCodes.missingParameter 太长了,也能够用如下方式:
  const { missingParameter, wrongParameterType, invalidToken, } = requestWrongCodes;
  switch (status) {
    case missingParameter:
      // Do something...
      break;
    case wrongParameterType:
      // Do something...
      break;
    case invalidToken:
      // Do something...
      break;
    default:
      throw (new Error('No have wrong code!'));
  }
};

修改后的代码就变得直观多了,由于状态码都被放到了 Enum 中统一管理,因此就能用常量名来表明它们,以后无论过了多久,能够明确的知道这里再作什么,甚至连注解或文档都不用写了,由于代码就是最好的文档。

善用 Enum 能使代码绝对是不可或缺的,但就算没使用 TypeScript 也别灰心,由于 TypeScript 最终会被转换为 JavaScript ,那来看看如何直接用 JavaScript 实现 Enum 吧!

用原生 JavaScript 实现 Enum

在前面说过 Enum 很像 Object ,若是研究一下 Enum 被编译成 javascript 以后的代码,就会发现还真的是 Object。

Enum 被编译后会变成 Key 和 Value 反向对应的对象,这样看起来很是简单,为了方便使用,下面把它的编译方式写成一个函数:

const newEnum = (descriptions) => {
  const result = {};
  Object.keys(descriptions).forEach((description) => {
    result[result[description] = descriptions[description]] = description;
  });
  return result;
};

const responseStatus = newEnum({
  error: 400,
  success: 200,
});

// { '200': 'success', '400': 'error', error: 400, success: 200 }
console.log(responseStatus);

虽然获得的结果相同,可是丧失了 Enum 中最难得的常量特点,若是不能让它变成不可修改,那就有可能会在代码里不经意地改动它,致使执行结果可能出错,因而能够在最后利用 Object.freeze() ,让外部操做没法新增、删除或从新定义任何 Property :

const newEnum = (descriptions) => {
  const result = {};
  Object.keys(descriptions).forEach((description) => {
    result[result[description] = descriptions[description]] = description;
  });
  return Object.freeze(result);
};

const responseStatus = newEnum({
  error: 400,
  success: 200,
});

// 即便不当心修改了
responseStatus['200'] = 'aaaaaaaa';

// 仍然是 { '200': 'success', '400': 'error', error: 400, success: 200 }
console.log(responseStatus);

这样就能简单在 JavaScript 中实现 Enum 了。

const Enum 的用法

从前面的 JavaScript 代码中能够看到 Enum 编译事后会变成 Key 和 Value 互相对应的 Object ,也就是说不论是用 Key 仍是Value 均可以取出对应的值,

可是若是用 const 声明 Enum ,编译以后就不会产生 Object。

直接看例子,假设我把 responseStateconst 从新生命,且也是以 handleResponseStatus 使用该 Enum 作判断:

enum responseStatus {
  error = 400,
  success = 200,
}

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case responseStatus.success:
      console.log('请求成功!');
      break;
    case responseStatus.error:
      console.log('请求失败!');
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

看起来一切正常,不过在编译后的 JavaScript 中,会发现 Enum 并无产生 Object ,而是直接用 const 声明在 Enum 中的值。

const 声明 Enum 有几个好处:

  1. 假设要用到的 Enum 很是多,那在执行时就会不停地使用 IIFE 产生 Object 将 Key 和 Value 绑定到 Object,会形成一些效率上的损失,也会增长内存,可是 const 并不会产生 Object ,也就不会有以上的问题。
  2. 就算到的 Enum 很少,判断时也须要一直从 Object 中找出对应的值,而若是是用 const 声明 Enum ,在编译成 JS 时就将声明的值直接放入判断中。

不过这样也就无法从 Enum 中反向取值了,由于它并不会产生对象:

const enum responseStatus {
  error = 400,
  success = 200,
}// 会出错,由于已经没有对象可供查找了
console.log(responseStatus[400])// 但这个不会有问题,由于编译的时候会直接填值
console.log(responseStatus.error)// 编译后:
// console.log(400)

173382ede7319973.gif


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章


欢迎继续阅读本专栏其它高赞文章:


相关文章
相关标签/搜索