iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
2
自我挑戰組

Typescript 初心者手札系列 第 13

【Day 13】TypeScript 資料型別 - 字面值型別(Literal Types) & 型別別名(Type Alias)

  • 分享至 

  • xImage
  •  

閱讀今天的文章前,先回顧一下昨天的學習,回答看看:

  • 什麼是元組(Tuple)?與陣列的差別?
  • 什麼是列舉(Enum)? 使用情境為何?
    如果有點不清楚答案的話,可以看看 Day12 的文章喔!

字面值型別(Literal Types)

字面值型別在 TS 1.8 版本時加入支援,一開始僅支援字串(String Literal Types),而後 2.0 版本開始支援數字字面值(Numeric Literal)列舉字面值(Enum literal types)布林值字面值(Boolean literal types)

字串字面值(String 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關鍵字進行宣告,例如:將聯合型別取了一個新別名叫 Direction

type 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 中有兩個參數,第二個參數值只能從四種允許的字符字面值選擇一種,倘若傳入其他值就會報錯,因此,可以幫助開發者避免拼寫錯誤。

字串字面值 v.s. 字串

引用 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 中字串字面值也是同樣的道理。

數字字面值(Numeric Literal Types)

和字串字面值類似,數字字面值可以限制變數的值為特定範圍的數字

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

布林字面值(Boolean Literal Types)

下面程式碼定義了兩個變數 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'

列舉字面值(Enum Literal Types)

沿用數字字面值的程式碼案例,同樣要做 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


上一篇
【Day 12】TypeScript 資料型別 - 元組(Tuple) & 列舉(Enum)
下一篇
【Day 14】TypeScript 資料型別 - 複合型別(Union & Intersection) & 型別檢測(Type Guard)
系列文
Typescript 初心者手札30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言