閱讀今天的文章前,先回顧一下昨天的學習,回答看看:
- 什麼是元組(Tuple)?與陣列的差別?
- 什麼是列舉(Enum)? 使用情境為何?
如果有點不清楚答案的話,可以看看 Day12 的文章喔!
字面值型別在 TS 1.8 版本時加入支援,一開始僅支援字串(String Literal Types),而後 2.0 版本開始支援數字字面值(Numeric Literal)
、列舉字面值(Enum literal types)
和布林值字面值(Boolean literal types)
。
用來限定字串變數只能使用列舉的字串值,例如:
let foo: 'Hello';
foo = 'Bar' //Error:Type '"Bar"' is not assignable to type '"Hello"'.
foo = 'Hello' // OK
foo = null //OK
foo = undefined //OK
我們創建了一個變數 foo ,型別為字串字面值'Hello',會發現這時候 foo 只能賦值為字串'Hello'、null 和 undefined (預設情況下 null 和 undefined 可以是任何型別的值,除非打開 strictNullChecks 設定),否則會報錯。在實際應用上,單純使用字串字面值的情境不多,但是字串字面值型別和聯合型別、型別別名結合,可以創造很不錯的應用。在說明範例之前,先來補充介紹 TS 的型別別名(Type Alias)
型別別名(Type Alias)
顧名思義就是用來重新命名一個型別,型別可以是任何型別,使用
type
關鍵字進行宣告,例如:將聯合型別取了一個新別名叫 Directiontype Direction = "North" | "East" | "South" | "West"
型別別名主要用來
簡化程式碼
以及讓型別含義變得更清楚
。上面的程式碼將字串字面值聯合型別抽象化為 Direaction 型別,讓開發者一看到就知道這是在定義方向的資料格式
當字串字面值型別和複合型別一起,可以用來描述有限的可能字串字面值(例如四種方向),舉例來說:
type Direction = "North" | "East" | "South" | "West";
function move(distance: number, direction: Direction) {
// ...
}
move(1,"North"); // Okay
move(1,"Nurth"); // Error!
上面的案例取四種方向的字串字面值聯集,並將這個聯集另取別名為 Direction,在函式 move 中有兩個參數,第二個參數值只能從四種允許的字符字面值選擇一種,倘若傳入其他值就會報錯,因此,可以幫助開發者避免拼寫錯誤。
引用 TS 的 github PR 的說明:
字串字面值
可以看做是字串
的子型別,換句話說,字串字面值可以賦值字串型別,反之則不然。
舉例來說:`
創建一個字串字面值型別的變數,並賦值字串,字串的方法和屬性都可正常使用
const eventName: "click" | "mouseover" = "click";
eventName.length; // 5
eventName.toUpperCase(); // "CLICK"
eventName + "!"; // "click!"
但是,相反的情況,將字串賦值給字串字面值就會報錯
const event: string = "something different";
const eventType: "click" | "mouseover" = event;
// Error: Type 'string' is not assignable to type '"click" | "mouseover"'. Type 'string' is not assignable to type '"mouseover"'.
其實,上面的邏輯並不難理解,就像是 "click" 屬於字串,但字串並不屬於"click"一樣,在 TS 中字串字面值也是同樣的道理。
和字串字面值類似,數字字面值可以限制變數的值為特定範圍的數字
let zeroOrOne: 0 | 1;
zeroOrOne = 0; // OK
zeroOrOne = 1; // OK
zeroOrOne = 2;
// Error: Type '2' is not assignable to type '0 | 1'
實際應用情境,例如處理 port 號,寫一個函式回傳可能的兩種結果
function getPort(scheme: "http" | "https"): 80 | 443 {
switch (scheme) {
case "http":
return 80;
case "https":
return 443;
}
}
const httpPort = getPort("http"); // 80
字面值型別也可以結合函式重載使用
function getPort(scheme: "http"): 80;
function getPort(scheme: "https"): 443;
function getPort(scheme: "http" | "https"): 80 | 443 {
switch (scheme) {
case "http":
return 80;
case "https":
return 443;
}
}
const httpPort = getPort("http"); // 80
const httpsPort = getPort("https"); // 443
下面程式碼定義了兩個變數 TRUE 和 FALSE
const TRUE: true = true
const FALSE: false = false
若將賦值對調就會報錯
const TRUE: true = false;
// Error: Type 'false' is not assignable to type 'true'
const FALSE: false = true;
// Error: Type 'true' is not assignable to type 'false'
沿用數字字面值的程式碼案例,同樣要做 port 號的處理,也可以寫成列舉字面值,並使用函式重載
const enum HttpPort {
Http = 80,
Https = 443
}
function getScheme(port: HttpPort.Http): "http";
function getScheme(port: HttpPort.Https): "https";
function getScheme(port: HttpPort): "http" | "https" {
switch (port) {
case HttpPort.Http:
return "http";
case HttpPort.Https:
return "https";
}
}
const scheme = getScheme(HttpPort.Http);
編譯之後的 JS 如下:
function getScheme(port) {
switch (port) {
case 80 /* Http */:
return "http";
case 443 /* Https */:
return "https";
}
}
const scheme = getScheme(80 /* Http */);
要注意的是,常數列舉在編譯時會將列舉的元素引用替換成其值,且不會產生查找物件。
今天介紹了幾種目前支援的字面值型別以及型別別名(Type Alias)的運作機制,明天我們來看今天有稍微提到的複合型別,那明天見囉!
參考資料:
TS 官方網站 - Advanced Types
String Literal Types in TypeScript
More Literal Types in TypeScript