iT邦幫忙

2024 iThome 鐵人賽

DAY 10
1
JavaScript

TypeScript 初學者也能看的學習指南系列 第 10

TypeScript 初學者也能看的學習指南 10 - Enum 列舉

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20240920/20149362hRH2rWAfhJ.png

本篇將來介紹 Enum,你會了解到什麼是 「Numeric Enums」, 「String Enums」,「計算成員」以及 Enum 中「反向查找」和「雙向映射」的特性

🚪 文章傳送門 🚪

Enum

Enum 是「Enumerated」的縮寫,中文翻為「列舉」、「枚舉」,是 TypeScript 獨有的型別

語法:enum <enum's name> { ... }

  • 使用 enum 關鍵字來定義
  • enum's name 和裡面的項目一般是「大寫開頭」,使用 PascalCase 命名法,例如:enum Days { Sun, Mon ... }
  • 適合用在具有「一定範圍內」的情境,例如:訂單狀態(已下單/運送中/已送達)、審核狀態(未審核/審核中/已審核/未通過)、權限管理(最高管理者/可編輯/可閱覽)...

Numeric Enums & String Enums

TypeScript 提供基於數字和字串的列舉

  • Numeric Enums 數字列舉

Enum 的列舉項被初始化時,會從 0 開始按順序持續遞增 1, 2, ...

enum Direction {
  Up,
  Down,
  Left,
  Right,
};

console.log(Direction.Up);    // 0
console.log(Direction.Down);  // 1
console.log(Direction.Left);  // 2
console.log(Direction.Right); // 3

也可以手動賦值,記得是用 =,有時候我會冒號、等於搞混XD
未手動賦值的列舉項,會承接著上一個列舉項的值繼續遞增

enum Direction {
  Up = 1,
  Down = 3,
  Left,
  Right,
};

console.log(Direction.Up);    // 1
console.log(Direction.Down);  // 3
console.log(Direction.Left);  // 4
console.log(Direction.Right); // 5

手動賦值後,假設在遞增個過程時,又出現重複的值,TypeScript 是「不會」報錯的,這點要特別留意~

enum Direction {
  Up = 2,
  Down = 1,
  Left,
  Right,
};

console.log(Direction.Up);    // 2
console.log(Direction.Down);  // 1
console.log(Direction.Left);  // 2 重複了!
console.log(Direction.Right); // 3

  • String Enums 字串列舉
    「字串列舉」就是每個列舉項都以string literal 字串字面量來初始化
    與「數字列舉」不同的是,「字串列舉」不會自動遞增值,若有一個項目的值沒有初始化,會報錯 ❌ Enum member must have initializer.
    此外,在語意上「字串列舉」比「數字列舉」更加明確、清晰
enum StrDirection {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT", // 把值拿掉會報錯 Enum member must have initializer.
}

「數字」和「字串」也是可以混合在同個 enum 裡喔~又稱為異構列舉(Heterogeneous enums)
雖然這樣做不會報錯,但官方並不建議,因為在語意和意義上沒什麼幫助,反而還可能讓程式變得更難理解與維護

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES",
}

接著,不論是「字串列舉」還是「數字列舉」都是屬於 constant enum expression 常數列舉表達式,可以使用 +, -, *, /, %, <<, >>, >>>, &, |, ^ 等二元運算子來計算常數

「constant enum expression 常數列舉表達式」會在編譯時 就被計算出,不用到 runtime(相反的 computed 屬性就會在 runtime 時計算)

以下範例僅供示意

enum Set {
  // constant members
  A,
  B = 5,
  C = 1 + 2,
  D = 1 - 3,
  E = 2 * 2
}

Enum 中包含計算(computed)成員

範例中,AB 都是屬於計算成員(在 runtime 時被計算),而 C 沒有明確的賦值(初始化),就會報錯
💡 如果列舉中有成員沒有初始化,它的位置要馬是在第一個,要馬就是在使用數字初始化的枚舉項之後

enum computedEnum {
  A = getSomeValue(),  // computed member
  B = "123".length,    // computed member
  C,  // ❌ Enum member must have initializer.
}


// 改成這樣就不會報錯了,範例僅供示意
enum E2 {
  C,                 // 0
  A = getSomeValue(),
  B = "123".length,
}

綜合以上來看

computed (計算)的列舉成員會在 runtime 時才被計算,因此無法在 compile 時就確定

constant (常數)的列舉成員在 compile 時就被計算

反向查找 & 雙向映射

反向查找就是指「我可以透過 value 來反查 key」,這個特性只有在 「數字列舉」才有

由下例可得知,我們能透過 value 來查找 key, 也可以透過 key 來查找 value,這稱為「雙向映射
也只有「數字列舉」才會產生「雙向映射」,「字串列舉」則是「單向映射

enum ReverseMapping {
  A,
};

console.log(ReverseMapping.A)  // 0
console.log(ReverseMapping[0]) // A


enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
console.log(Direction.Up);     // UP
console.log(Direction["UP"]);  // undefined

其他範例

  • 任務狀態
enum TaskStatus {
  Pending,
  InProgress,
  Completed,
};
let tasks: {title: string, status: TaskStatus}[] = [];  // 格式為陣列包物件

function title (title: string) {
  tasks.push({
    title,
    status: TaskStatus.Completed,
  });
};

title('寫鐵人賽');
console.log('tasks', tasks); // [ { title: '寫鐵人賽', status: 2 } ]

總結

  • Enum 的使用時機為有限制在一定範圍內的場景
  • Enum 在編譯後,都會產生對應的 JavaScript 物件,所以它也是屬於 Object Types
項目 數字列舉 字串列舉
初始化 每個成員初始化為數字,未初始化則自動從 0 開始遞增 每個成員初始化為字串字面量
雙向映射 無,為單向映射
可讀性 較低 較高
範例 enum Status { Active } enum Status { Active = "ACTIVE" }

每天講的內容有推到 github 上喔

References


上一篇
TypeScript 初學者也能看的學習指南 09 - Function Overloads 函式重載
下一篇
TypeScript 初學者也能看的學習指南 11 - 常數列舉(const Enums) & 外部列舉(Ambient Enums)
系列文
TypeScript 初學者也能看的學習指南16
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言