29、枚举

枚举

枚举使用场景

枚举类型 用于取值被限定在一定范围内的场景。

demo: 一周只能有七天,颜色限定为红黄绿

枚举语法及特性

javascript 复制代码
// 简单例子
enum Days {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday,
}

特性:

枚举成员会被赋值为从0开始递增的数字,同时也会对枚举值到枚举名进行反向映射

javascript 复制代码
enum Days {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday,
}
console.log(Days['Monday']=== 0) // true
console.log(Days['Tuesday']=== 1) // true

console.log(Days[5] === 'Saturday') // true
console.log(Days[6] === 'Sunday') // true
javascript 复制代码
enum Days {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday,
}
// js输出为
var Days;
(function (Days) {
    Days[Days["Monday"] = 0] = "Monday";
    Days[Days["Tuesday"] = 1] = "Tuesday";
    Days[Days["Wednesday"] = 2] = "Wednesday";
    Days[Days["Thursday"] = 3] = "Thursday";
    Days[Days["Friday"] = 4] = "Friday";
    Days[Days["Saturday"] = 5] = "Saturday";
    Days[Days["Sunday"] = 6] = "Sunday";
})(Days || (Days = {}));
// 上述函数自执行后,得到如下对象
{
0: "Monday"
1: "Tuesday"
2: "Wednesday"
3: "Thursday"
4: "Friday"
5: "Saturday"
6: "Sunday"
Friday: 4
Monday: 0
Saturday: 5
Sunday: 6
Thursday: 3
Tuesday: 1
Wednesday: 2
}

手动给枚举赋值

1、未手动赋值的枚举项会接着上一个枚举项递增,如下demo

javascript 复制代码
enum Color {
 A = 7,
 B = 1,
 C,
 D,
 E,
 F,
 G
}
console.log(Color['A'] === 7) // true
console.log(Color['B'] === 1) // true
console.log(Color['C'] === 2) // true
console.log(Color['G'] === 6) // true

手动赋值项和未手动赋值项重复

未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点,并没有什么报错,也没有进行一些相关的提示

javascript 复制代码
enum Color {
 A = 3,
 B = 1,
 C,
 D,
 E,
 F,
 G
}
console.log(Color['A'] === 3) // true
console.log(Color['D'] === 3) // true
console.log(Color[3] === 'A') // false
console.log(Color[3] === 'D') // true

上面的例子中,递增到 3 的时候与前面的 A 的取值重复了,但是 TypeScript 并没有报错,导致 Days[3] 的值先是 "A",而后又被 "D" 覆盖了。编译的结果是

上述的ts编译为js,如下:

javascript 复制代码
var Color;
(function (Color) {
    Color[Color["A"] = 3] = "A";
    Color[Color["B"] = 1] = "B";
    Color[Color["C"] = 2] = "C";
    Color[Color["D"] = 3] = "D";
    Color[Color["E"] = 4] = "E";
    Color[Color["F"] = 5] = "F";
    Color[Color["G"] = 6] = "G";
})(Color || (Color = {}));

所以实际使用中,要避免上述情况的出现

手动赋值项智能赋值数字?NO

手动赋值的枚举项可以不是数字,此时需要使用类型断言来让 tsc 无视类型检查 (编译出的 js 仍然是可用的)

javascript 复制代码
enum Color1 {
  A = 1,
  B ,
  C ,
  D ,
  E ,
  F = <any>'S'
}
console.log(Color1['A'] === 1) // true
console.log(Color1['D'] === 4) // true
console.log(Color1[4] === 'D') // true
console.log(Color1['F'] === 'S') // true
console.log(Color1['S'] === 'F') // true
// 会报错:TS2367: This comparison appears to be unintentional because the types 'Color1' and 'string' have no overlap
// 这种比较似乎是无意的,因为类型'Color1'和'string'没有重叠


// 编译后
var Color1;
(function (Color1) {
    Color1[Color1["A"] = 1] = "A";
    Color1[Color1["B"] = 2] = "B";
    Color1[Color1["C"] = 3] = "C";
    Color1[Color1["D"] = 4] = "D";
    Color1[Color1["E"] = 5] = "E";
    Color1[Color1["F"] = 'S'] = "F";
})(Color1 || (Color1 = {}));
console.log(Color1['A'] === 1);
console.log(Color1['D'] === 4);
console.log(Color1[4] === 'D');
console.log(Color1['F'] === 'S');
console.log(Color1['S'] === 'F');

当给枚举值赋值小数时,依次递增的特性是正常的,步长为1

javascript 复制代码
enum Color2 {
  A = 7,
  B = 1.3,
  C ,
  D
}
console.log(Color2['A'] === 7) // true
console.log(Color2['C'] === 2.3) // true
console.log(Color2['D'] === 3.3)// true

// 编译后的js
var Color2;
(function (Color2) {
    Color2[Color2["A"] = 7] = "A";
    Color2[Color2["B"] = 1.3] = "B";
    Color2[Color2["C"] = 2.3] = "C";
    Color2[Color2["D"] = 3.3] = "D";
})(Color2 || (Color2 = {}));

常数项和计算项

前面的demo一直都是常数项

下方demo中 'zhang'.length 就是一个计算项

计算属性放在枚举最后面是没问题,通过js可以看到,还是正常的

javascript 复制代码
enum Color3 {
  A ,
  B ,
  C ,
  D = 'zhang'.length
}
console.log(Color3['A'] === 0) // true
console.log(Color3['D'] === 5) // true
// 编译后
var Color3;
(function (Color3) {
    Color3[Color3["A"] = 0] = "A";
    Color3[Color3["B"] = 1] = "B";
    Color3[Color3["C"] = 2] = "C";
    Color3[Color3["D"] = 'zhang'.length] = "D";
})(Color3 || (Color3 = {}));
console.log(Color3['A'] === 0);
console.log(Color3['D'] === 5);

计算属性放在枚举最前面,可能会导致一些问题:如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错(ts提示枚举值必须有初始值)

javascript 复制代码
enum Color3 {
  A = 'zhang'.length,
  B ,
  C ,
  D 
}
console.log(Color3['A'] === 5) // true
console.log(Color3['D'] === 8) // false
// 编译报错:Enum member must have initializer  枚举成员必须有初始化式

// 编译后
var Color3;
(function (Color3) {
    Color3[Color3["A"] = 'zhang'.length] = "A";
    Color3[Color3["B"] = void 0] = "B";
    Color3[Color3["C"] = void 0] = "C";
    Color3[Color3["D"] = void 0] = "D";
})(Color3 || (Color3 = {}));
console.log(Color3['A'] === 5);
console.log(Color3['D'] === 8);

下面是常数项和计算所得项的完整定义:

当满足以下条件时,枚举成员被当作是常数:

1、不具有初始化函数并且之前的枚举成员是常数。在这种情况下,当前枚举成员的值为上一个枚举成员的值加 1。但第一个枚举元素是个例外。如果它没有初始化方法,那么它的初始值为 0。

2、枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集,它可以在编译阶段求值。当一个表达式满足下面条件之一时,它就是一个常数枚举表达式:

  • 数字字面量
  • 引用之前定义的常数枚举成员(可以是在不同的枚举类型中定义的)如果这个成员是在同一个枚举类型中定义的,可以使用非限定名来引用
  • 带括号的常数枚举表达式
  • +, -, ~ 一元运算符应用于常数枚举表达式
  • +, -, *, /, %, <<, >>, >>>, &, |, ^ 二元运算符,常数枚举表达式做为其一个操作对象。若常数枚举表达式求值后为 NaN 或 Infinity,则会在编译阶段报错

所有其它情况的枚举成员被当作是需要计算得出的值。

常数枚举

常数枚举是使用 const enum 定义的枚举类型

常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员,如下编译后的代码可以看下(普通枚举编译后是一个自执行函数)

javascript 复制代码
  Red ,
  Green ,
  Blue
}
let kate = [Color4.Red, Color4.Green, Color4.Blue]
console.log(kate)
// 编译之后
let kate = [0 /* Color4.Red */, 1 /* Color4.Green */, 2 /* Color4.Blue */];
console.log(kate);

常数枚举中,不能含有计算计算成员

以下情况会报错:

javascript 复制代码
const enum Color4 {
  Red ,
  Green ,
  Blue = 'blue'.length
}
let kate = [Color4.Red, Color4.Green, Color4.Blue]
console.log(kate)
// 编译会报错
// error TS2474: const enum member initializers must be constant expressions.

// 编译后 编译后的js也是没办法运行的
let kate = [0 /* Color4.Red */, 1 /* Color4.Green */, Color4.Blue];
console.log(kate);

外部枚举

1、外部枚举(Ambient Enums)是使用 declare enum 定义的枚举类型
2、declare定义的类型只会用于编译时的检查,编译结果实际是会被删除的
3、外部枚举与声明语句一样,常出现的声明文件中

javascript 复制代码
declare  enum Color5 {
  A,
  B,
  C,
  D
}
let Dire =[Color5.A, Color5.B, Color5.C, Color5.D]
console.log(Dire)
// 编译后
let Dire = [Color5.A, Color5.B, Color5.C, Color5.D];
console.log(Dire);
// 编译后的上述文件,肯定是运行失败的,Color就没有去定义怎么成功?

同时使用 declare 和 const 也是可以外部枚举的

javascript 复制代码
declare const enum Color6 {
  A,
  B,
  C
}
let kate = [Color6.A, Color6.B, Color6.C]
// 编译后
let kate = [0 /* Color6.A */, 1 /* Color6.B */, 2 /* Color6.C */];

最后备注一下:其实枚举类型的概念是来源于c#

相关推荐
jonjia6 小时前
模块、脚本与声明文件
typescript
jonjia6 小时前
配置 TypeScript
typescript
jonjia6 小时前
TypeScript 工具函数开发
typescript
jonjia6 小时前
注解与断言
typescript
jonjia6 小时前
IDE 超能力
typescript
jonjia6 小时前
对象类型
typescript
jonjia6 小时前
快速搭建 TypeScript 开发环境
typescript
jonjia6 小时前
TypeScript 的奇怪之处
typescript
jonjia6 小时前
类型派生
typescript
jonjia6 小时前
开发流程中的 TypeScript
typescript