详解 TypeScript 枚举类型


Posted in Javascript onNovember 02, 2021

前言:

TypeScript 在 ES 原有类型基础上加入枚举类型,使得在 TypeScript 中也可以给一组数值赋予名字,这样对开发者比较友好,可以理解枚举就是一个字典。

枚举类型使用enum来定义:

enum Day {
  SUNDAY,
  MONDAY,
  TUESDAY,
  WEDNESDAY,
  THURSDAY,
  FRIDAY,
  SATURDAY
 }

上面定义的枚举类型的Day,它有7个值,TypeScript会为它们每个值分配编号,默认从0开始,在使用时,就可以使用名字而不需要记数字和名称的对应关系了:

enum Day {
  SUNDAY = 0,
  MONDAY = 1,
  TUESDAY = 2,
  WEDNESDAY = 3,
  THURSDAY = 4,
  FRIDAY = 5,
  SATURDAY = 6
}

下面是将上面代码转译为 JavaScript 后的效果:

var Day = void 0;
(function (Day) {
  Day[Day["SUNDAY"] = 0] = "SUNDAY";
  Day[Day["MONDAY"] = 1] = "MONDAY";
  Day[Day["TUESDAY"] = 2] = "TUESDAY";
  Day[Day["WEDNESDAY"] = 3] = "WEDNESDAY";
  Day[Day["THURSDAY"] = 4] = "THURSDAY";
  Day[Day["FRIDAY"] = 5] = "FRIDAY";
  Day[Day["SATURDAY"] = 6] = "SATURDAY";
})(Day || (Day = {}));

可以看到,每一个值都被赋予了对应的数字。

在TypeScript中,我们需要通过点的形式获取枚举集合中的成员:

console.log(Day.SUNDAY)   // 0
console.log(Day.MONDAY)   // 1

说完枚举类型的基本使用,下面就来看一下常见的枚举类型。

1. 数字枚举

在上面的例子中,在仅指定常量命名的情况下,定义的就是一个默认从 0 开始递增的数字集合,称之为数字枚举。如果想要从其他值开始递增,可以将第一个值的索引值进行指定:

enum Color {
  Red = 2,
  Blue,
  Yellow
}
console.log(Color.Red, Color.Blue, Color.Yellow); // 2 3 4

可以对一个字段指定一个索引值,那他后面没有指定索引值的就会依次加一:

// 指定部分字段,其他使用默认递增索引
enum Status {
  Ok = 200,
  Created,
  Accepted,
  BadRequest = 400,
  Unauthorized
}
console.log(Status.Created, Status.Accepted, Status.Unauthorized); // 201 202 401

除此之外,还可以给每个字段指定不连续的任意索引值:

enum Status {
  Success = 200,
  NotFound = 404,
  Error = 500
}
console.log(Status.Success, Status.NotFound, Status.Error); // 200 404 500

数字枚举在定义值时,可以使用计算值和常量。但是要注意,如果某个字段使用了计算值或常量,那么该字段后面紧接着的字段必须设置初始值,这里不能使用默认的递增值了,来看例子:

// 初值为计算值
const getValue = () => {
  return 0;
};
enum ErrorIndex {
  a = getValue(),
  b, // error 枚举成员必须具有初始化的值
  c
}
enum RightIndex {
  a = getValue(),
  b = 1,
  c
}
// 初值为常量
const Start = 1;
enum Index {
  a = Start,
  b, // error 枚举成员必须具有初始化的值
  c
}

2. 字符串枚举

TypeScript 将定义值是字符串字面量的枚举称为字符串枚举,字符串枚举值要求每个字段的值都必须是字符串字面量,或者是该枚举值中另一个字符串枚举成员:

// 使用字符串字面量
enum Message {
  Error = "Sorry, error",
  Success = "Hoho, success"
}
console.log(Message.Error); // 'Sorry, error'

// 使用枚举值中其他枚举成员
enum Message {
  Error = "error message",
  ServerError = Error,
  ClientError = Error
}
console.log(Message.Error); // 'error message'
console.log(Message.ServerError); // 'error message'

注意:这里的其他枚举成员指的是同一个枚举值中的枚举成员,因为字符串枚举不能使用常量或者计算值,所以不能使用其他枚举值中的成员。

3. 反向映射

定义枚举类型的值时,可以通过 Enum['key'] 或者 Enum.key 的形式获取到对应的值 valueTypeScript 还支持反向映射,但是反向映射只支持数字枚举,不支持字符串枚举。

来看下面的例子:

enum Status {
  Success = 200,
  NotFound = 404,
  Error = 500
}
console.log(Status["Success"]); // 200
console.log(Status[200]); // 'Success'
console.log(Status[Status["Success"]]); // 'Success'

TypeScript 中定义的枚举,编译之后其实是一个对象,生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( name -> value)和反向映射( value -> name)。

下面来看看上面代码中的 Status 编译后的效果:

{
    200: "Success",
    404: "NotFound",
    500: "Error",
    Error: 500,
    NotFound: 404,
    Success: 200
}

可以看到,TypeScript 会把定义的枚举值的字段名分别作为对象的属性名和属性值,把枚举值的字段值分别作为对象的属性值和属性名,同时添加到对象中。这样既可以通过枚举值的字段名得到值,也可以通过枚举值的值得到字段名。

4. 异构枚举

异构枚举就是枚举值中成员值既有数字类型又有字符串类型,如下:

enum Result {
  Faild = 0,
  Success = "Success"
}

在开发过程中不建议使用异步枚举。因为往往将一类值整理为一个枚举值时,它们的特点是相似的。比如在做接口请求时的返回状态码,如果是状态码都是数值,如果是提示信息,都是字符串,所以在使用枚举的时候,往往是可以避免使用异构枚举的,主要是做好类型的整理。

5. 常量枚举

TypeScript中,定义了枚举值之后,编译成 JavaScript 的代码会创建一个对应的对象,这个对象可以在程序运行时使用。但是如果使用枚举只是为了让程序可读性好,并不需要编译后的对象呢?这样会增加一些编译后的代码量。TypeScript 中有一个const enum(常量枚举),在定义枚举的语句之前加上const关键字,这样编译后的代码不会创建这个对象,只是会从枚举里拿到相应的值进行替换:

enum Status {
  Off,
  On
}
const enum Animal {
  Dog,
  Cat
}
const status = Status.On;
const animal = Animal.Dog;

上面的代码编译成 JavaScript 之后是这样的:

var Status;
(function(Status) {
  Status[(Status["Off"] = 0)] = "Off";
  Status[(Status["On"] = 1)] = "On";
})(Status || (Status = {}));
var status = Status.On;
var animal = 0; // Dog

对于 Status 的处理,先是定义一个变量 Status,然后定义一个立即执行函数,在函数内给 Status 添加对应属性,首先Status[“Off”] = 0是给Status对象设置Off属性,并且值设为 0,这个赋值表达式的返回值是等号右边的值,也就是 0,所以Status[Status[“Off”] = 0] = "Off"相当于Status[0] = “Off” 。创建了这个对象之后,将 Status 的 On 属性值赋值给 status;再来看下 animal 的处理,编译后的代码并没有像Status创建一个Animal对象,而是直接把Animal.Dog的值0替换到了const animal = Animal.Dog表达式的Animal.Dog位置。

通过定义常量枚举,可以以清晰、结构化的形式维护相关联的常量集合。而且因为转译后抹除了定义、内联成员值,所以在代码的体积和性能方面并不会比直接内联常量值差。

6. 枚举成员类型和联合枚举类型

如果枚举值里所有成员都是字面量类型的值,那么枚举的每个成员和枚举值本身都可以作为类型来使用,我们称这样的枚举成员为字面量枚举成员。

满足条件的枚举成员的值有以下三种:

  • 没有初始值的枚举成员,例如:enum E { A }
  • 值为字符串字面量,例如:enum E { A = 'a' }
  • 值为数值字面量,或者带有-符号的数值字面量,例如:enum E { A = 1 }、enum E { A = -1 }

(1)枚举成员类型

当所有枚举成员都拥有字面量枚举值时,就枚举成员成为了类型:

enum Animal {
  Dog = 1,
  Cat = 2
}

interface Dog {
  type: Animal.Dog; 
}
interface Cat {
  type: Animal.Cat; 
}

let cat: Cat = {
  type: Animal.Dog // error [ts] 不能将类型“Animal.Dog”分配给类型“Animal.Cat”
};
let dog: Dog = {
  type: Animal.Dog
};

可以看到,代码的第七行使用Animal.Dog作为类型,指定接口Dog的必须有一个type字段,且类型为Animal.Dog

(2)联合枚举类型

当枚举值符合条件时,这个枚举值就可以看做是一个包含所有成员的联合类型:

enum Status {
  Off,
  On
}
interface Light {
  status: Status;
}
enum Animal {
  Dog = 1,
  Cat = 2
}
const light1: Light = {
  status: Animal.Dog // error 不能将类型“Animal.Dog”分配给类型“Status”
};
const light2: Light = {
  status: Status.Off
};
const light3: Light = {
  status: Status.On
};

上面例子定义接口 Light status 字段的类型为枚举值 Status,那么此时 status 的属性值必须为 Status.Off 和 Status.On 中的一个,也就是相当于status: Status.Off | Status.On

7. 枚举合并

说完常见的枚举类型,最后来看看枚举合并的概念。对于枚举类型的值,我们可以分开进行声明:

enum Day {
  SUNDAY,
  MONDAY,
  TUESDAY
 }

enum Day {
  WEDNESDAY,
  THURSDAY,
  FRIDAY,
  SATURDAY
 }

这时 TypeScript 就会对这个枚举值进行合并操作,合并后编译为JavaScript的代码如下:

var Day = void 0;
(function (Day) {
  Day[Day["SUNDAY"] = 0] = "SUNDAY";
  Day[Day["MONDAY"] = 1] = "MONDAY";
  Day[Day["TUESDAY"] = 2] = "TUESDAY";
  Day[Day["WEDNESDAY"] = 3] = "WEDNESDAY";
  Day[Day["THURSDAY"] = 4] = "THURSDAY";
  Day[Day["FRIDAY"] = 5] = "FRIDAY";
  Day[Day["SATURDAY"] = 6] = "SATURDAY";
})(Day || (Day = {}));

到此这篇关于详解 TypeScript 枚举类型的文章就介绍到这了,更多相关TypeScript 枚举类型内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
滚动经典最新话题[prototype框架]下编写
Oct 03 Javascript
Javascript 判断 object 的特定类转载
Feb 01 Javascript
ko knockoutjs动态属性绑定技巧应用
Nov 14 Javascript
node.js中使用socket.io的方法
Dec 15 Javascript
JS给Textarea文本框添加行号的方法
Aug 20 Javascript
jquery动态添加文本并获取值的方法
Oct 12 Javascript
原生js实现网易轮播图效果
Apr 10 Javascript
基于slideout.js实现移动端侧边栏滑动特效
Nov 28 Javascript
JS返回只包含数字类型的数组实例分析
Dec 16 Javascript
Vue项目查看当前使用的elementUI版本的方法
Sep 27 Javascript
vue弹出框组件封装实例代码
Oct 31 Javascript
加速vue组件渲染之性能优化
Apr 09 Javascript
前端JavaScript大管家 package.json
JavaScript 原型与原型链详情
javascript实现计算器功能详解流程
JS创建或填充任意长度数组的小技巧汇总
Oct 24 #Javascript
一文彻底理解js原生语法prototype,__proto__和constructor
Oct 24 #Javascript
javascript遍历对象的五种方式实例代码
Oct 24 #Javascript
低门槛开发iOS、Android、小程序应用的前端框架详解
Oct 16 #Javascript
You might like
用PHP实现将GB编码转换为UTF8
2006/11/25 PHP
PHP 身份证号验证函数
2009/05/07 PHP
Yii2 队列 shmilyzxt/yii2-queue 简单概述
2017/08/02 PHP
总结一些js自定义的函数
2006/08/05 Javascript
用Javascript实现锚点(Anchor)间平滑跳转
2009/09/08 Javascript
jquery 动态创建元素的方式介绍及应用
2013/04/21 Javascript
浅谈 jQuery 事件源码定位问题
2014/06/18 Javascript
原生javascript实现简单的datagrid数据表格
2015/01/02 Javascript
jQuery拖拽插件gridster使用指南
2015/04/21 Javascript
jQuery实现仿Alipay支付宝首页全屏焦点图切换特效
2015/05/04 Javascript
javascript实现树形菜单的方法
2015/07/17 Javascript
JavaScript的React Web库的理念剖析及基础上手指南
2016/05/10 Javascript
AngularJS入门教程之链接与图片模板详解
2016/08/19 Javascript
js内置对象处理_打印学生成绩单的简单实现
2016/09/24 Javascript
JS识别浏览器类型(电脑浏览器和手机浏览器)
2016/11/18 Javascript
AngularJS打开页面隐藏显示表达式用法示例
2016/12/25 Javascript
bootstrap laydate日期组件使用详解
2017/01/04 Javascript
Linux系统中利用node.js提取Word(doc/docx)及PDF文本的内容
2017/06/17 Javascript
express+mockjs实现模拟后台数据发送功能
2018/01/07 Javascript
JavaScript 复制对象与Object.assign方法无法实现深复制
2018/11/02 Javascript
详解koa2学习中使用 async 、await、promise解决异步的问题
2018/11/13 Javascript
AngularJS实现的鼠标拖动画矩形框示例【可兼容IE8】
2019/05/17 Javascript
简单了解微信小程序 e.target与e.currentTarget的不同
2019/09/27 Javascript
一篇文章带你搞懂Vue虚拟Dom与diff算法
2020/08/25 Javascript
[01:04:30]Fnatic vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
Python实现的下载8000首儿歌的代码分享
2014/11/21 Python
Python上下文管理器和with块详解
2017/09/09 Python
python模拟鼠标点击和键盘输入的操作
2019/08/04 Python
如何在python中写hive脚本
2019/11/08 Python
Django中modelform组件实例用法总结
2020/02/10 Python
Python更换pip源方法过程解析
2020/05/19 Python
德国高尔夫商店:Golfshop.de
2019/06/22 全球购物
什么叫应用程序域?什么是受管制的代码?什么是强类型系统?什么是装箱和拆箱?
2016/08/13 面试题
学校师德承诺书
2014/05/23 职场文书
理财计划书
2014/08/14 职场文书
详解MySQL中的主键与事务
2021/05/27 MySQL