跳至主要内容

[Day11] TS:什麼!型別也有樣板字串(Template Literal Types)?

template literal types

這是我們今天要聊的內容,老樣的,如果你已經可以輕鬆看懂,歡迎直接左轉去看我同事 Kyle 精彩的文章 — 「今晚,我想來點 Web 前端效能優化大補帖!」。

Template Literal Types 的基本用法

大部分 JavaScript 的開發者都知道有 Template Literals / Template Strings(樣本字面值)可以使用,講中文可能大家反而不知道是什麼,但重點就是使用反引號(backtick)` 這個關鍵字,並可以在 ${} 裡面帶入變數,像是這樣:

const occupation = 'Web Developer';
const content = `Hello, I am a ${occupation}.`; // 'Hello, I am a Web Developer.'

而這個用法同樣可以在 TypeScript 中定義型別時使用,稱作 Template Literal Types。就讓我們直接把上面 JavaScript 的範例改成型別看看:

type Occupation = 'Web Developer';
type Content = `Hello, I am a ${Occupation}.`; // "Hello, I am a Web Developer."

用起來就和 JavaScript 的 Template Literals 一樣,就是這麼簡單!

這裡 OccupationContent 這種直接用字串來定義型別的方式稱作 String Literal Types

當 Template Literal Types 碰到聯集(union)

在 TypeScript 中,Template Literal Types 除了可以像 JavaScript 直接帶入變數外,還可以帶入聯集型別(union types),而這是個非常實用的功能。

舉例來說:

type HandledEvent = 'change' | 'click' | 'keydown';
type EventHandler = `on${HandledEvent}`; // "onchange" | "onclick" | "onkeydown"

在上面的例子中可以看到 HandledEvent 是一個 Union Type,當我們把 Union Type 放到 Template Literal Types 中時,TypeScript 會根據所傳入的聯集,自動組成新的聯集(即,"onchange" | "onclick" | "onkeydown"),這個感覺有點類似前幾天提到的分配律

現在讓我們回到今天一開始的例子:

type X = 'left' | 'right';
type Y = 'top' | 'bottom';
type Position = `${X}-${Y}`;

現在讀者應該可以理解 Position 的型別最終會得到什麼。因為 X 傳入的是聯集 "left" | "right",而 Y 也同樣是聯集 "top" | "bottom",因此最終 Position 這個型別就會是 "left-top" | "left-bottom" | "right-top" | "right-bottom"

透過 Template Literal Types 這樣的特性可以幫助開發者方便快速的產生所需要的型別。

補充:透過 Template Literal Types 將 enum 的 values 變成 Union Type

在 TypeScript 中很常會只用到 enum,像是:

enum MANUFACTURE {
APPLE = 'apple',
SAMSUNG = 'samsung',
GOOGLE = 'google',
SONY = 'sony',
}

有時我們想要根據 enum 的 values 來產生出新的型別,如果透過手動定義的話很麻煩,會是這樣:

type Manufacture = 'apple' | 'samsung' | 'google' | 'sony';

而且未來如果 MANUFACTURE 有添加新的內容時,還要記得回過頭來修改 Manufacture 這個型別,非常不方便。

當有這種需求時,實際上可以使用 Template Literal Types 來解決:

type Manufacture = `${MANUFACTURE}`; // "apple" | "samsung" | "google" | "sony"

就是這麼簡單,一行搞定!

範例程式碼

https://tsplay.dev/WzoKew @ TypeScript Playground

參考資料