數字枚舉
數字枚舉,即枚舉里所有屬性的值都是數字類型,先看這段代碼:
enum Colors { Red, Blue, Yellow } console.log(Colors.Red) // 0 console.log(Colors.Blue) // 1 console.log(Colors.Yellow) // 2
之前也提到過,當枚舉里的屬性沒指定具體值時,默認值是從 0 開始依次排列,你也可以自己指定具體值,剩下的也是依次遞增:
enum Colors { Red, Blue = 5, Yellow } console.log(Colors.Red) // 0 console.log(Colors.Blue) // 5 console.log(Colors.Yellow) // 6
另外,數字枚舉在定義值的時候,可以是 常量 或者是 計算出來的。當滿足以下條件時,枚舉成員可以看作常量:
1. 它是枚舉的第一個成員且沒有初始化,這種情況下它被賦予值 0。
enum Colors { Red }
2. 它未初始化器且它之前的枚舉成員是一個 數字常量。 這種情況下,當前枚舉成員的值為它上一個枚舉成員的值加1。
enum List1 { X, Y, Z } enum List2 { A = 1, B, C } // List1中沒初始化,X滿足第一個條件,是常量 0,Y 和 Z 沒初始化且之前的成員是 數字常量,所以List1所有成員都是常量 // List2 中A直接指定為常量 1,B,C 遞增為2,3,List2中也都是常量
3. 枚舉成員使用 常量枚舉表達式初始化。當一個表達式滿足下面條件之一時,它就是一個常量枚舉表達式:
- 一個枚舉表達式字面量(主要是字符串字面量或數字字面量)
- 一個對之前定義的常量枚舉成員的引用(可以是在不同的枚舉類型中定義的)
- 帶括號的常量枚舉表達式
- 一元運算符
+,-,~其中之一應用在了常量枚舉表達式 - 常量枚舉表達式做為二元運算符
+,-,*,/,%,<<,>>,>>>,&,|,^的操作對象。 若常數枚舉表達式求值后為NaN或Infinity,則會在編譯階段報錯。
所有其它情況的枚舉成員被當作是需要計算得出的值。例如:
const getValue = () => { return 0 } enum List { A = getValue(), B = 2, // 此處必須要初始化值,不然編譯不通過 C } console.log(List.A) // 0 console.log(List.B) // 2 console.log(List.C) // 3
A的值是被計算出來的。注意注釋部分,如果某個屬性的值是計算出來的,那么它后面一位的成員必須要初始化值。
反向映射
我們可以通過 Enum[key] 或者 Enum.key 的方式獲取到對應的值。typescript 還支持反向映射,即可以通過值來獲取鍵,不過反向映射只支持數字枚舉。下面是個例子:
enum Status { Success = 200, NotFound = 404, Error = 500 } console.log(Status.Success) // 200 console.log(Status[200]) // Success console.log(Status[Status.Success]) // Success
字符串枚舉
字符串枚舉值要求每個字段的值都必須是字符串字面量,或者是另外一個字符串枚舉成員,如下:
enum Str { Str1 = 'this is string one', Str2 = 'this is string two', Str3 = Str1 // 這里引用了Str1的值 } console.log(Str.Str1) // this is string one console.log(Str.Str2) // this is string two console.log(Str.Str3) // this is string one
異構枚舉
異構枚舉是指,枚舉可以混合字符串和數字成員,如:
enum Enum { A = 0, B = 'hello' } console.log(Enum.A) // 0 console.log(Enum.B) // hello
這種混合值類型的枚舉通常是不需要的,不建議使用。因為往往我們將一類值整理為一個枚舉時,它們的特點都是相似的。比如我們在做接口請求時的返回狀態碼,如果是狀態碼都是數值,如果是提示信息,都是字符串。
枚舉成員類型和聯合枚舉類型
如果一個枚舉里所有成員的值都是字面量類型的值,那么這個枚舉的每個成員和枚舉本身都可以作為類型來使用。字面量枚舉成員需滿足以下條件:
- 不帶初始值的枚舉成員,例如 enum E { A }
- 值為字符串字面量,例如 enum E { A = 'hello' }
- 值為數字字面量,或者帶有一元
-符號的數字字面量,例如 enum E { A = 1 },enum E { A = -1 }
(1)枚舉成員類型
把符合條件的枚舉成員作為類型來使用,例子:
enum ShapeKind { Circle, Square } interface Circle { kind: ShapeKind.Circle // 使用 ShapeKind.Circle 作為類型,指定接口須有 kind 字段,且類型為 ShapeKind.Circle radius: number } interface Square { kind: ShapeKind.Square // 同上 sideLength: number } let c: Circle = { kind: ShapeKind.Square, // Error! 因為接口 Circle 的 kind 被指定為 ShapeKind.Circle類型,所以這里會報錯 radius: 100 }
interface是定義接口,現在簡單了解即可。
(2)聯合枚舉類型
符合條件的枚舉本身可以看作是一個包含所有成員的聯合類型,下面例子:
// 枚舉 Status 里有兩個狀態 enum Status { Off, On } // 枚舉 Animal 里有兩個動物 enum Animal { Cat, Dog } // 接口 Light 中定義 status字段,它是 Status 類型,可以是 Off 或者 On 狀態 interface Light { status: Status } let lg1: Light = { status: Status.Off // 正確 } let lg2: Light = { status: Animal.Cat // error 不能將類型 Animal.Cat 分配給類型 Status }
運行時的枚舉
枚舉是在運行時真正存在的對象,可以把枚舉當作對象使用:
enum E { A, B } function func(obj: { A: number }): number { return obj.A } console.log(func(E)) // 0
代碼中,聲明了一個函數 func,它的參數是一個對象,且必須包含屬性名為 A 的屬性,A 的值為數值類型。當調用函數 func 時,把枚舉 E 當作符合條件的實參傳入,正確運行。
const enum
定義的枚舉,在經過編譯器編譯后是一個對象,這個對象我們可以在程序運行時使用,前面有說到。但有時定義枚舉可能只是為了讓程序可讀性更好,而不需要編譯后的代碼,即不需要編譯成對象。typescript中考慮到這種情況,所以加入了 const enum (完全嵌入的枚舉)。typescript官網有一個在線編譯器,來看看下面的例子:
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一樣創建了Animal,而是直接把 Animal 中 Dog 值 0 替換到表達式中 Animal.Dog 的位置,這樣就節省了生成代碼的開銷。
