官方定义
使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。枚举使用 enum
关键字来定义。
理解:
枚举是列举固定几个值,用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。
直接定义变量的话的话可以随意定义,枚举只能使用你定义好的几个值
例如你定义一个方法接收一个参数,这个参数如果是int型,别人在调用你这个方法时可以传1或2等任意数字,虽然你告诉了同事,只能传0或1,
如果你把参数定义成只能传某个类型,例如是个枚举类,那么别人只能传你枚举类里定义好的几个类型,传其它的就会在编译时期报错。
按照枚举成员分类(数字、字符串、异构)
一、数字枚举
例1
enum Direction {
Up, // 0
Down,// 1
Left,// 2
Right// 3
}
例2
enum Direction {
Up = 1,// 1
Down,// 2
Left,// 3
Right// 4
}
例3
enum Direction {
Up,// 0
Down=3,// 3
Left,// 4
Right// 5
}
例4
enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true
以上四个例子我们可以看出
- 数字枚举如果没有初始化,默认初始化值为0,每项+1
- 如果有初始化,则在初始化值的基础上,每项+1
- 如果某项被赋值(可以是负数或负数),那么之后的项在此基础上+1
- 如果未手动赋值的枚举项与手动赋值的重复了,如例4,TypeScript 是不会察觉到这一点的,但建议尽量避免
反向映射
除了创建一个以属性名做为对象成员的对象之外,数字枚举成员(字符串枚举成员没有反向映射)还具有了 反向映射,从枚举值到枚举名字
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true
二、字符串枚举
在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
三、异构枚举
将数字枚举与字符串枚举混用,但不建议
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
按照声明方式(普通枚举,const 枚举,外部枚举,外部常量枚举)
一、普通枚举
上述例子都是常数项
值由计算所得变为计算所得项
如,"blue".length
就是一个计算所得项
enum Color {Red, Green, Blue = "blue".length};
以下情况不被允许
enum Color {Red = "red".length, Green, Blue};
根据官方定义,不带初始化器的枚举要么被放在第一的位置,要么被放在使用了数字常量或其它常量初始化了的枚举后面。
上述代码会报错,因为Red是计算项,而Green紧接其后却无法获取初始值
二、常数枚举 (const enum
定义的枚举)
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
会被编译为
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员,假如包含了计算成员,则会在编译阶段报错。
三、外部枚举(declare enum
定义的枚举)
种声明枚举的方式比较特别,使用的时候需要非常谨慎,该枚举类型不会生成反向映射
// 声明语 + 关键词 + 枚举名称
declare enum ChineseZodiac {
rat = 1,
cattle,
tiger,
rabbit,
dragon
}
console.log(ChineseZodiac)
console.log(ChineseZodiac.rat)
你会发现,无论你是访问枚举本身还是内部成员,均会报错: ChineseZodiac is not defined
编译之后并没有生成该枚举,也就是说,声明的外部枚举是没有被编译的,导致在 runtime 的时候就会报错
这就让人很头疼,既然不能访问,那为何要能做出这个声明呢。
官网对其的描述是:外部枚举用来描述已经存在的枚举类型的形状,这样听起来很晦涩。
通过代码来分析
- 外部枚举是为了描述当前环境中已经存在的对象的,这个对象可以存在任意的地方,但是一定是已声明的;
1-1- 一个.html
后缀文件,为了引入.ts
文件编译之后的结果,用于调试 -
1-2-.ts
源文件 -
1-3- 编译之后的.js
文件 -
1-4- 其他.js
资源文件 -
1-5- 访问结果 -
会发现是不会报错的,但是你可能会疑问了,这个不就是访问自己声明的一个变量吗,那跟.ts
文件中声明的枚举有什么关系?图样图森破,外部枚举类似于ts
的类型断言,只要在开发中有这个声明,意味着在当前开发环境上下文中一定存在当前这个对象,你可以随意使用当前对象;当然也就意味着你声明外部枚举的时候慎重,我是否真的需要这样做,不然runtime
使用的时候就出错了; - 外部枚举还可以防止声明枚举的命名冲突和成员冲突
2-1- 我在上面文件结构基础上新增了一个enum.ts
文件,并在里面声明了一个普通枚举,但是枚举成员和外部枚举成员相同
2-2- 之所以会有这样的提示,是declare
的作用,因为ts
类型系统能够侦测到当前整个文件目录上下文中的所有declare
声明的变量,编译器也会有语法提示;
四、外部常量枚举(declare 和 const 关键词联合声明)
这个枚举类型和 const
枚举类型并没有什么区别,只是会提示是否有枚举命名冲突和成员冲突,该枚举类型不会生成反向映射
// 声明语 + 修饰符 + 关键词 + 枚举名称
declare const enum ChineseZodiac {
rat = 1,
cattle,
tiger,
rabbit,
dragon
}
ChineseZodiac.dragon => 5/* dragon */