聊聊TypeScript中列舉物件(Enum)

蛙人 發表於 2021-10-15

前言

什麼是列舉,顧名思義我們看到列舉這個詞,腦子裡就已經想到這是一個鍵值對的形式,就可以看做是我們JavaScript中的json物件一樣。在ts中宣告列舉物件的關鍵字是使用enum,列舉成員一但定義就不可改變了。下面來介紹一下ts中的列舉物件。

數字列舉

我們先來說一下數字列舉, 一目瞭然,一看就知道數字列舉的成員都是number型別的。下面我們來看看建立一個簡單的列舉物件吧。

enum device {
    phone,
    notebook,
    desktop
}

console.log(device.phone) // 0

上面的值是phone 0、notebook 1、desktop 2。

只要定義了列舉物件,預設不賦值,那麼當前的列舉物件裡面第一個的值從0開始依次遞增。

看下面栗子

enum device {
  phone = 3,
  notebook,
  desktop
}

上面我們預設給phone賦值一個3,那麼下面兩個值也會預設遞增,會變成notebook = 4desktop = 5

注意事項 - 列舉物件遞增

  • 列舉物件成員遞增值為1
  • 列舉物件成員遞增只會看當前值的前一個列舉成員是否有值,有值的話後面依次跟著遞增。跟第一個列舉成員值無關

數字列舉物件會存在反向對映

enum device {
      phone
      notebook,
      desktop
}

通過上面栗子我們可以知道,device.notebook = 1。但還可以通過device[1]獲取出來notebook,這就是因為存在反向對映(key和value可以互相訪問)。

需要注意的是,只有數字列舉成員才會有反向對映,字串或其它值是沒有的。

來看下列舉物件被編譯後的

<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd33df8cc99f4517907ffa807af4f973~tplv-k3u1fbpfcp-watermark.image" width="100%"/>

我們再來看一個問題

enum device {
    phone = 2,
    notebook = 1,
    desktop
}

console.log(device.phone) // 2
console.log(device[2]) // desktop

可以看到上面程式碼中,device.phone = 2 ,然後我們使用反向這種方式訪問device[2]卻是desktop。這是因為預設我們給phone賦值為2,然後又給notebook賦值為1,這時desktop跟著上一個列舉成員(notebook)值去遞增,所以這時2已經被替換為desktop了。

這裡需要注意,避免踩坑,ts是不會檢查出來的重複的值。

字串列舉

看過了上面的數字列舉,再來看字串列舉,顯然就明白字串列舉成員的值肯定都是string型別。字串列舉物件是沒有反向對映的。

enum device {
    phone = "1",
    notebook = "2",
    desktop = "3"
}

我們來看一下編譯後的程式碼

<img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0e79c440bf4efc99c404019ea3abed~tplv-k3u1fbpfcp-watermark.image" width="100%"/>

可以看到上面編譯後的程式碼沒有反向對映的程式碼。

字串列舉是沒有遞增的,當前的列舉成員前一個值為字串,那麼當前的列舉物件如果不賦值就會報錯。

enum device {
    phone = "1",
    notebook
}

上面這種情況就會導致編譯階段報錯。"列舉成員必須具有初始化表示式",待會我們就會講到列舉物件的表示式

異構列舉

異構列舉是啥呢,直白的說就是一個列舉物件中可以包括數字列舉成員字串列舉成員,就是可以混合使用。但是我們要正兒八經的使用列舉物件的話,一般還真不會使用異構列舉,就像文件說的一樣,似乎你並不會這麼做。

enum Person {
    name = "前端娛樂圈",
    age = 18
}

在看一個栗子

enum Person {
    name = "前端娛樂圈",
    age = 3 * 6
}

上面這種是會報錯的,“含字串值成員的列舉中不允許使用計算值”,列舉物件成員有字串的則不能再設定其它列舉物件成員為計算的值(3 * 6)。但是可以直接寫字面量的。下面我們會講到計算

計算的和常量成員

列舉物件中的列舉成員都帶有一個值,這個值是計算的常量。那麼怎麼看是計算的還是常量呢。這就講到我們上面說的列舉物件成員表示式,只要是表示式那一定就是常量否則就是計算的。所以只需要知道列舉成員是表示式就知道它就是常量。

當滿足下面其中一個條件那麼它就是一個表示式。以下借用官方的規則條件

  • 一個列舉表示式字面量(主要是字串字面量或數字字面量)
  • 一個對之前定義的常量列舉成員的引用(可以是在不同的列舉型別中定義的)
  • 帶括號的常量列舉表示式
  • 一元運算子 +, -, ~其中之一應用在了常量列舉表示式
  • 常量列舉表示式做為二元運算子 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作物件。 若常數列舉表示式求值後為 NaNInfinity,則會在編譯階段報錯。

上面這些條件成立之後那麼當前列舉成員就是一個常量。常量就是可以在編譯階段求值的

常量

enum obj {
    index, // 滿足條件 常量
    index1 = index, // 滿足條件 常量
    age = 2 << 1, // 滿足條件 常量 
    num = 30 | 2, // 滿足條件 常量
    num1 = 10 + 29 // 滿足條件 常量
}

在看一下上面編譯後的程式碼,可以看到直接在編譯階段求值了。

<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42beb700d9cd4d0a81d756d12384910b~tplv-k3u1fbpfcp-watermark.image" width="100%"/>

計算的

enum obj {
    nameLen = "前端娛樂圈".length, // 計算的
    num = Math.random() * 100 // 計算的
}

在看一下計算的編譯後的程式碼是沒有求值的。

<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b517b83eba894ce88c1973b77478f37f~tplv-k3u1fbpfcp-watermark.image" width="100%"/>

const列舉

一般情況下,普通列舉物件就可以滿足我的需求,但是有些情況比如為了節省額外的開銷和效能,我們可以選擇使用常量列舉,常量列舉使用const關鍵字定義,它與普通列舉不同的時,它會在編譯階段刪除該物件,且不能訪問該列舉物件,只能訪問該列舉物件成員。常量列舉的成員只能是常量列舉表示式,不可以使用計算值

const enum obj {
    A = 1,
    B = 3 * 6,
    C = 1 & 2
}

console.log(obj) // 報錯
需要注意上面這個常量列舉物件編譯後的物件也是"空"的,系統自動刪除
const enum obj {
    A = 1,
    B = 3 * 6,
    C = 1 & 2
}

console.log(obj.A) // 1
console.log(obj.B) // 8
console.log(obj.C) // 0

那麼上面這個栗子,編譯後只能看見console.log這些值。

<img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c34317c5a0c54d8a8ac34f9823c16a21~tplv-k3u1fbpfcp-watermark.image" width="100%"/>

外部列舉

外部列舉使用declare關鍵字定義,文件描述:外部列舉用來描述已經存在的列舉型別的形狀,意思就是說外部列舉用來描述當前環境中存在的列舉物件。外部列舉和普通列舉的一個區別就是,在外部列舉裡面沒有初始化的列舉成員會當成一個計算值,而在普通列舉裡面則是一個常量

declare enum Enum {
    A = 1,
    B,
    C = 2
}

console.log(Enum);
console.log(Enum.A)

上面這種執行完,你會發現不管執行列舉本身還是列舉成員都是報錯,"Enum is not defined"。因為外部列舉編譯後壓根就沒有生成。所以有明白外部列舉用途的小夥伴給解答一下,謝謝啦~

列舉成員的型別

列舉成員也可以被當做一個型別,就是可以指定某些變數的值必須是列舉成員的值。

列舉成員成為型別必須滿足其中之一條件

  • 字面量列舉成員是指不帶有初始值的常量列舉成員
  • 任何字串字面量(如:AB
  • 任何數字字面量(如: 1, 100
  • 應用了一元 -符號的數字字面量(如: -1, -100
enum obj {
    name = "前端娛樂圈",
    num = -100
}

interface msg {
    title: obj.name;
    num: obj.num
}

let json: msg = {
    title: obj.name,
    num: obj.num
}

感謝

謝謝閱讀,如有幫助請點個關注、收藏吧

覺得有幫助可以關注 前端娛樂圈 公眾號,每天為你推送一篇小知識

大家也可以加我 微信 交個朋友,可以找我聊天或拉你進技術交流群