详解 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 相关文章推荐
JavaScript 嵌套函数指向this对象错误的解决方法
Mar 15 Javascript
获取body标签的两种方法
Oct 13 Javascript
jQuery源码分析-01总体架构分析
Nov 14 Javascript
基于JavaScript自定义构造函数的详解说明
Apr 24 Javascript
JQuery实现级联下拉框效果实例讲解
Sep 17 Javascript
Jquery uploadify上传插件使用详解
Jan 13 Javascript
解析javascript图片懒加载与预加载的分析总结
Oct 27 Javascript
Java中int与integer的区别(基本数据类型与引用数据类型)
Feb 19 Javascript
使用Vue-cli 3.0搭建Vue项目的方法
Jun 07 Javascript
koa大型web项目中使用路由装饰器的方法示例
Apr 02 Javascript
关于layui flow loading占位图的实现方法
Sep 21 Javascript
JavaScript实现轮播图效果代码实例
Sep 28 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
打造计数器DIY三步曲(上)
2006/10/09 PHP
PHP_MySQL教程-第一天
2007/03/18 PHP
php PDO中文乱码解决办法
2009/07/20 PHP
php getsiteurl()函数
2009/09/05 PHP
php 无法加载mysql的module的时候的配置的解决方案引发的思考
2012/01/27 PHP
最新最全PHP生成制作验证码代码详解(推荐)
2016/06/12 PHP
利用PHP如何写APP接口详解
2016/08/23 PHP
用js生产批量批处理执行命令
2008/07/28 Javascript
JavaScript 面向对象之命名空间
2010/05/04 Javascript
jQuery 借助插件Lavalamp实现导航条动态美化效果
2013/09/27 Javascript
js 判断图片是否加载完以及实现图片的预下载
2014/08/14 Javascript
jQuery入门介绍之基础知识
2015/01/13 Javascript
浅谈javascript中的闭包
2015/05/13 Javascript
D3.js实现饼状图的方法详解
2016/09/21 Javascript
微信公众号开发 自定义菜单跳转页面并获取用户信息实例详解
2016/12/08 Javascript
jQuery焦点图轮播效果实现方法
2016/12/19 Javascript
微信小程序 数据交互与渲染实例详解
2017/01/21 Javascript
jQuery中ajax获取数据赋值给页面的实例
2017/12/31 jQuery
vue添加class样式实例讲解
2019/02/12 Javascript
nodejs检测因特网是否断开的解决方案
2019/04/17 NodeJs
Angular请求防抖处理第一次请求失效问题
2019/05/17 Javascript
node.js 微信开发之定时获取access_token
2020/02/07 Javascript
JS错误处理与调试操作实例分析
2020/04/13 Javascript
基于PHP pthreads实现多线程代码实例
2020/06/24 Javascript
深入探究Python中变量的拷贝和作用域问题
2015/05/05 Python
详解Python使用tensorflow入门指南
2018/02/09 Python
用Eclipse写python程序
2018/02/10 Python
超简单使用Python换脸实例
2019/03/27 Python
豪华复古化妆:Besame Cosmetics
2019/09/06 全球购物
大四本科生的自我评价
2013/12/30 职场文书
八一建军节营销活动方案
2014/08/31 职场文书
大学生思想道德自我评价
2015/03/09 职场文书
大学生党员暑假实践(活动总结)
2019/08/21 职场文书
浅谈Golang 切片(slice)扩容机制的原理
2021/06/09 Golang
SQL实现LeetCode(178.分数排行)
2021/08/04 MySQL
《原神》新角色演示“神里绫人:林隐泓洄” 宠妹狂魔
2022/04/03 其他游戏