enum


Posted by TempuraEngineer on 2022-12-02

目錄


enum是什麼

列舉(enum)是Typescript的一個功能,可用於定義一系列被命名的常數,其key常為upper camel case

列舉當作型別時,型別會是其值

enum Weekday {
    Monday = 'Mon',
    Tuesday = 'Tue',
    Wednesday = 'Wed',
    Thursday = 'Thur',
    Friday = 'Fri',
    Saturday = 'Sat'
}

const today:Weekday = Weekday.Monday;

// Type '"Tue"' is not assignable to type 'Weekday'.
const tomorrow:Weekday = 'Tue';


Numeric enum & String enum

Numeric enum

enum Direction {
  Top = 1,
  Bottom,
  Left,
  Right,
}

以這個例子來說Top被賦予初始值1,其餘的3個會自動依序+1

如果不想賦予Top初始值也可以,那它們就會從0開始依序+1

另外和string enum不同的點除了自動遞增的功能外,其內的成員的值不必都是常數,常數與計算值可以並存


String enum

enum Fruit {
  Apple = 'apple',
  Banana = 'banana',
}

string enum的概念和numeric enum差不多,但可讀性較高

內部的成員必須賦予常數來初始化,這個常數可以是string,也可以是另一個string enum的成員


Heterogeneous enum

enum Mix {
  Up = 1,
  Down = 'down'
}

雖然官方不推薦這麼做,但其實一個enum裡可以同時有number和string


常數 & 計算值列舉(constant & computed enum)

常數

一個常數enum的成員只能以constant enum expression初始化

而constant enum expression需要符合以下任一條件

  1. 字串或數值
  2. 指向另一個常數enum的reference,且被指向的enum需要定義在前面
  3. 被括號包起來的constant enum expression (a parenthesized constant enum expression)
  4. 使用一元運算子(ex: +、-、~)做constant enum expression的運算 (unary operators applied to constant enum expression)
  5. 使用二進位運算子(ex: +、-、&、|、<<)做constant enum expression的運算 (binary operators with constant enum expressions as operands)

另外需要注意的是如果constant enum expression的值被判斷為NaN或Infinity報錯

enum Fruit {
  // constant
  Banana,
  Orange = 1 << 1,
  Tangerinr = 1 << 2,
  Citrus  = Orange | Tangerinr,

  // computed
  WaterMelon = 'watermelon'.length,
}

使用起來會像這樣

interface LunchBox {
    main: 'rice' | 'spagetti',
    fruit: Fruit.Citrus
}

const myLunchBox: LunchBox = {
    main: 'rice',
    // 會報錯Type 'Fruit.WaterMelon' is not assignable to type 'Fruit.Citrus'.
    fruit: Fruit.WaterMelon  
}


運行 & 編譯期間

運行期間

enum在運行時可以視為物件,因為Typescript被轉為Javascript後會把enum轉為物件,

它可以當做參數傳給函式

function foo(obj: {Banana: number}){
    return obj.Banana;
}

// Fruit有Banana這個屬性,且型別為number,所以這樣是可以的
foo(Fruit);


編譯期間

前面說到在運行時enum會被視為物件,只有在編譯時才會被視為型別

這一段我們來來看看typeof 與 keyof這兩個運算子一起用會怎樣

先來看看單獨使用keyof

keyof是個只能在Typescript使用的運算子,它能把物件型別的key抽出來

enum Fruit {
  Orange = 'o',
  Tangerinr = 't',
  Citrus = 'c',
}

interface LunchBox {
    main: 'rice' | 'spagetti',
    fruit: Fruit.Citrus
}

// 相當於'main' | 'fruit'
type LunchContent = keyof LunchBox;

再來看看單獨使用typeof

type FruitKinds = typeof Fruit;

const c:FruitKinds = Fruit;

// Type 'Fruit' is not assignable to type 'typeof Fruit'
const d:FruitKinds = Fruit.Orange;

Type 'number' is not assignable to type 'typeof Fruit'
const e:FruitKinds = 1;

從以上我們可以得知typeof Fruit就是型別Fruit,所以型別是FruitKinds的變數其值只能是Fruit

最後在來看看兩者合前我們先看一個例子,這個例子是抽出物件的key,然後把這些key變成一個型別

const student = {
  name:'Alex',
  studentId:'st1'
};

// 先用typeof student取得型別{name: string; studentId: string}
// 再來用keyof把物件型別的key抽出來
// 相當於'name' | 'studentId'
type StudentNeed = keyof typeof student;

const foo:StudentNeed = 'name';
const bar:StudentNeed = 'name';

// Type '"skill"' is not assignable to type '"name" | "studentId"'.
const fooBar:StudentNeed = 'skill';

接著來看合用

// 因為typeof Fruit就是型別Fruit
// 使用keyof把物件型別的key抽出來
// 所以相當於'Orange' | 'Tangerinr' | 'Citrus'
type FruitNames = keyof typeof Fruit;

const foo:FruitNames = 'Orange';

// Type '"Potato"' is not assignable to type '"Orange" | "Tangerinr" | "Citrus"'.
const bar:FruitNames = 'Potato';

那如果不使用typeof呢🤔

// Fruit會被視為物件,而一個物件底下有一些方法跟屬性(ex: toString, length)

type FruitNames = keyof Fruit;

const foo:FruitNames = 'toString';
const bar:FruitNames = 'slice';

// Type '"Orange"' is not assignable to type 'number | unique symbol | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | ... 28 more ... | "padEnd"'.
const fooBar:FruitNames = 'Orange';


參考資料

Typescript - enum
TypeScript - 簡單易懂的keyof typeof 分析


#TypeScript #enum #列舉







Related Posts

W12_作業二實作記錄 [ MTR05 ] 實作之四

W12_作業二實作記錄 [ MTR05 ] 實作之四

路由器router 路由設計routing 路由route

路由器router 路由設計routing 路由route

MTR04_0930

MTR04_0930


Comments