枚举类型是很多语言都拥有的类型,用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。

数字枚举

当声明一个枚举类型时,不给其赋值。此时它们的值是默认的数字类型,而且默认从0开始依次累加。

  1. enum D {
  2. a,
  3. b,
  4. c,
  5. d
  6. }
  7. console.log(D.a === 0) // true
  8. console.log(D.b === 1// true
  9. console.log(D.c === 2 // true
  10. console.log(D.d === 3) // true

而当把第一个值赋值以后,后面的也会根据第一个值进行累加:

  1. enum D {
  2. a = 10,
  3. b,
  4. c,
  5. d
  6. }
  7. console.log(D.a,D.b,D.c,D.d); // 10,11,12,13

字符串枚举

枚举类型的值可谓字符串类型

  1. enum D {
  2. a = 'up',
  3. b = 'down',
  4. c = 'left',
  5. d = 'right'
  6. }
  7. console.log(D[d],D.a); // right up

异构枚举

混用字符串和数字枚举,从技术角度是可行的。

  1. enum booleanEnum {
  2. No = 0,
  3. Yes = 'yes'
  4. }

反向映射

由以上可知,当我们获取枚举值的时候可通过枚举名字来获取。反向映射是指,通过枚举值来反向获取枚举的名字。(javascript中对象一般都是正向映射的,即name => value,而在枚举中可以正反同时映射,即name <=> value,想要理解这种正反同时映射的特性,需了解下节枚举的本质)

  1. enum D {
  2. a,
  3. b,
  4. c,
  5. d
  6. }
  7. console.log(D[0]); // a

枚举的本质

枚举类型被编译为JavaScript后的样子如下:

  1. var D;
  2. (function (D) {
  3. D[D['a'] = 10] = 'up';
  4. D[D['b'] = 11] = 'down';
  5. D[D['c'] = 12] = 'left';
  6. D[D['d'] = 13] = 'right';
  7. })(D || D = {})

编译后的代码坑看起来比较复杂,不过可以把D看成一个对象。

  1. enum D {
  2. a = 10,
  3. b,
  4. c,
  5. d
  6. }
  7. console.log(D[10],D[d]);// a 13

原因在编译后的JavaScript中体现出来了,因为D[D[‘a’]=10] = ‘a’,即D[10] = ‘a’;所以我们可以把类型看做是一个JavaScript对象,而由于其特殊的构造,导致其拥有正反向同时映射的特性。

常量枚举

枚举可以被const 声明为常量。

  1. const enum D {
  2. a = 'a',
  3. b = 'b',
  4. c = 'c',
  5. d = 'd'
  6. }
  7. const a = D.a;

猜猜被编译为JavaScript后是怎么样的?

  1. var a = "a"

在上面看到枚举类型会被编译为JavaScript对象,而这里没有了。
这就是常量枚举的作用,因为下面的变量a已经使用过了枚举类型,之后就没用了,故没有必要存在于JavaScript中,typescript在这一步就把D去掉了,直接使用D的值即可,这是性能提升的一个方案!

如果非要typescript保留对象D,那么可以添加编译选项 —preserveConstEnums

联合枚举与枚举成员的类型

假设枚举的所有成员都是字面量类型的值,那么枚举的每个成员和枚举值本身都可以作为类型来使用。

  • 任何字符串字面量

    1. const enum D {
    2. a = 'a',
    3. b = 'b'
    4. }
  • 任何数字字面量

    1. enum D {
    2. a,
    3. b
    4. }
  • 应用了一元-符号的数字字面量

    1. enum D {
    2. a = -1,
    3. b = -2
    4. }

    枚举成员类型

    当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义,即枚举成员成为了类型。
    比如声明一个数字类型:

    1. enum D {
    2. a,
    3. b,
    4. c,
    5. d
    6. }
    7. const a = 0;
    8. console.log(a === D.a) // true

    把成员当做值使用,是没有问题的,因为成员值本身就是0,那么,再加几行代码:

    1. type c = 0
    2. declare let b: c
    3. b = 1 // 不能将类型“1”分配给类型“0”
    4. b = D.a // ok

    由此可见,枚举的成员可以被当做类型使用,这就是枚举成员当做类型使用的情况。

    联合枚举类型

    由于联合联合枚举,类型系统key知道枚举里的值的集合。

    1. enum D{
    2. a,
    3. b,
    4. c,
    5. d
    6. }
    7. declare let a:D
    8. enum Animal {
    9. Dog,
    10. Cat
    11. }
    12. a = D.a // ok
    13. a = Animal.Dog // 不能将类型Animal.dog 分配给类型“D”

    我们把a 声明为D类型,可以看成我没声明了一个联合类型D.a | D.b | D.c | D.d,只有这四个类型其中的成员才符合要求。

    枚举合并

    我们可以分开声明枚举,它们会自动合并 ```typescript enum D { a = ‘a’, b = ‘b’, c = ‘c’, d = ‘d’ }

enum D { center = 1 }

  1. 编译为JavaScript代码如下:
  2. ```javascript
  3. var D;
  4. (function()D {
  5. D['a'] = 'a';
  6. D['b'] = 'b';
  7. D['c'] = 'c';
  8. D['d'] = 'd';
  9. })(D || D = {});
  10. (function (D){
  11. D[D['center'] = 1] = 'center';
  12. })(D || D = {})

因此以上并不冲突。

为枚举添加静态方法

借助namespace命名空间,我们可以给枚举添加静态方法。
举个栗子:假设有十二个月份:

  1. enum month {
  2. Jan,
  3. Feb,
  4. Marth,
  5. April,
  6. May,
  7. June,
  8. July,
  9. August,
  10. September,
  11. October,
  12. November,
  13. December
  14. }

要编写一个静态方法,找出夏天:

  1. function isSummber(month:month) {
  2. switch(month){
  3. case month.June:
  4. case month.July:
  5. case month.August:
  6. return true;
  7. default: return false
  8. }}

想要把两者结合,就需要借助命名空间的力量:

  1. namespace month {
  2. export function isSummber(month:month) {
  3. switch(month) {
  4. case month.june:
  5. case month.july:
  6. case month.august:
  7. return true;
  8. default: return false
  9. }}
  10. }
  11. console.log(month.issummer(month.january));//false