1. 枚举的作用是列举类型中包含的各个值,一般用它来管理多个相同系列的常量(即不能被修改的变量),用于状态的判断。这是一种无序的数据结构,把键映射到值上,可以理解为编译时键固定的对象,访问键时,ts将检查指定的键是否存在

在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!'));
  }
};

刚刚的200,304等都是没有争议的状态码,但是实际开发过程中往往后台人员会自己定义一些字符串标识状态:

  • 接手项目的程序员不去翻阅文档的话根本不知道这是啥玩意
  • 容易造成魔鬼字符串的问题
  • 用enum可以很好地解决这个问题
enum requestStatusCodes {
    error = '400',
    success = '200',
}
enum requestWrongCodes {
    missingParameter = 'A',
    wrongParameterType = 'B',
    invalidToken = 'C'
}

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!'));
  }
};
  1. 枚举分为两种:字符串到字符串之间的映射和字符串到数字之间的映射。即key和value反向对应的对象,如下所示:
enum Language {  //枚举名称为大写的单数形式
    English,   //枚举中的键也为大写
    Spanish,
    Russian
}
console.log(Language);
//ts自动推导出来的Language如下
{
  '0': 'English',
  '1': 'Spanish',
  '2': 'Russian',
  English: 0,    
  Spanish: 1,    
  Russian: 2     
}
  1. 枚举中的值使用点号或者方括号
let myFirstLanguage = Language.English
let mySecondLanguage = Language['Spanish']
console.log(myFirstLanguage, mySecondLanguage); //0 1
  1. 一个枚举可以分成几次声明,ts会自动把各部分合并在一起,注意,如果分开声明枚举,ts只能推导出其中一部分的值,因此最好为枚举中的每个成员显式赋值
enum Language {  
    English = 0,  
    Spanish = 1,
}
enum Language {
    Russian = 2
}
  1. 不必为枚举中的所有成员都赋值,ts会尽其所能推导出缺少的值:
enum Language { 
    English = 1,   
    Spanish = 100 + 1,
    Russian  //ts推导为102
}
console.log(Language);
  1. 枚举的值可以为字符串,甚至混用字符串和数字
enum Color {
    Red = '#c10000',
    Blue = '#007ac1',
    Pink = 'oxc10050',
    White = 255
}
let red = Color['Red']
let white = Color.White
console.log(red, white);  //#c10000 255
  1. ts比较灵活,允许通过值访问枚举,也允许通过键访问枚举,不过容易导致问题
enum Language {  
    English,   
    Spanish,
    Russian,
}
console.log(Language[0], Language[1]);  //English Spanish

在这里插入图片描述


Language[‘Chinese’]和 Language[6]明明都不存在,为什么前者报错后者不报错呢?

  1. 利用js实现enum
  • 实现key-value的反向对应的对象
  • 不可修改
const newEnum = (descriptions) => {
    const result = {}
    Object.keys(descriptions).forEach(description => {
        console.log(result[description] = descriptions[description]);//注意,等号这个会返回等于的值
        result[result[description] = descriptions[description]] = description;
    });
    return Object.freeze(result)
}
let responseCode = newEnum({
    error: 400,
    success: 200
})
console.log(responseCode);

可以看到,enum本质上是生成键值对双向映射的对象,所以访问color[6]按照obj的惯性,是不会报错的,之后返回undefined
其实Color[6]不存在,但是TypeScript并不阻止你这么做。为了避免这种不安全的访问操作:

  1. 可以通过const enum指定使用枚举的安全子集。下面使用该方法重写前面的Language枚举
  • const enum 不允许通过键反向查找,行为和常规的js对象很像
  • 另外,默认不编译生成任何js代码(就像第八点生成的那个result对象),而是在用到枚举成员的地方内插对应的值,例如,把Lauguage.Spanish直接替换成1

在这里插入图片描述

  • 使用const emun的好处,加入需要使用的enum特别多,那在执行时就会不停地使用 IIFE 产生 Object 将 Key 和 Value 绑定到 Object,会造成一些效率上的损失,也会增加内存,但是 const 并不会产生 Object ,也就不会有以上的问题。
  • 就算到的 Enum 不多,判断时也需要一直从 Object 中找出对应的值,而如果是用 const 声明 Enum ,在编译成 JS 时就将声明的值直接放入判断中。
  1. const enum的内插行为带来的安全问题
  • const enum内插值的行为在从其他人编写的TypeScript代码中导入const enum时可能导致安全问题:假如原作者在你编译TypeScript 代码之后更新了const enum,那么在运行时你使用的枚举与原作者的枚举指向的值可能不同,而TypeScript没有这么智能,无法得知这一变化。
  • 使用const enum时请尽量避免内插,而且只在受自己控制的TypeScript程序中使用。不要在计划发布到NPM中的程序,或者开放给其他人使用的库中使用。

如果想为const enum生成运行时代码,在 tsconfig.json中把TSC选项preserveConstEnums设为true:

参考:

  1. 书籍-《typescript编程》
  2. 木庄的博客
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐