本篇要來介紹「Literal Types」以及「Template Literal Types」的概念
並附上範例參考
Literal (adj) 字面的、根本的
Literal Types
中文翻譯為「明文型別」、「字面量(值)型別」,主要是用來限定某些變數或參數的值必須是字面上的值
,當今天想賦予字面值以外的型別就會出錯
用一句話來總結:
It is what it looks 它就是它看起來的樣子 👀
來直接看範例吧!
let a1 = 'May'; // : string
let b1 = 1; // : number
let c1 = null; // : null
let d1 = undefined; // : undefined
let e1 = true; // : boolean
let f1 = Symbol('test'); // : symbol
let g1 = { name: 'banana' }; // : { name: string; }
let h1 = [1, 2, 3]; // : number[]
let j1 = function test() {}; // : () => void
const a2 = 'May'; // : May
const b2 = 1; // : 1
const c2 = null; // : null
const d2 = undefined; // : undefined
const e2 = true; // : true
const f2 = Symbol('test'); // : typeof f2
const g2 = { name: 'banana'}; // : { name: string; }
const h2 = [1, 2, 3]; // : number[]
const j2 = function test() {}; // : () => void
用 let 宣告,型別推論 a1
的型別為 string
用 const 宣告,型別推論 a2
的型別為 May
,換句話說 May 是一個 Literal Types
a1
和 a2
值都是 May
,為什麼會有這樣的差異?
這是因為 let 可以被重新賦值的關係,即使初始的值是一個字面量,TypeScript 也會將變數的類型推斷為該字面量的基本類型(例如: string
、number
、boolean
)
const 的值在初始後就不能被改變,因此 TypeScript 會推論此變數值為 Literal Types
以上試驗的結果並不代表不能用 let 去宣告 Literal Types
透過型別註釋,直接指定字面量的值
也是可以做到,看看底下例子吧!
x
的 Literal Types 為 hello
let x: 'hello';
x = 'hello'; // ✅ Pass
x = 'typescript'; // ❌ Type '"typescript"' is not assignable to type '"hello"'.
flag
的 Literal Types 為 yes
or no
let flag: 'yes' | 'no';
flag = 'no'; // ✅ Pass
flag = 'maybe'; ❌ Type '"maybe"' is not assignable to type '"yes" | "no"'.
其實在賦值的時候會貼心跳出提示告訴你,值只能是 'yes' or 'no'
interface
interface SquareConfig {
width: number;
}
function setSquare(x: SquareConfig | 'auto') {
console.log(x);
}
setSquare({ width: 50 }); // ✅ Pass
setSquare("auto"); // ✅ Pass
setSquare("automatic"); // ❌ ...
object, function, array, tuple, enum...都代表一種型別,廣義來說都是Literal Types
的表示方式
可以將 Literal Types
視為「眾多表示方式的集合體」
const str = 'May'; // : May
let str2: 'hello' = 'hello'; // : hello
const n1 = 10; // : 10
let n2: 100 = 100; // : 100
const v1 = true; // : true
let v2: true = true; // : true
test1
推論出來的結果並不是 : object
,而是 : { ... }
test1
和 test2
都是 Object Literal Types 的一種let test1 = { name: 'banana'}; // : { name: string; }
let test2: {name: '11'} = {name: '11'} // { name: '11' }
let fruits: ('apple' | 'banana')[] = ['apple', 'banana']; // : ('apple' | 'banana')[]
let tuple: ['a', 'b'] = ['a','b'] // : ['a','b']
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
let move: Direction = Direction.Up; // : Direction
function getStatus(): 'success' | 'failure' {
return 'success'; // 只能回傳 success 或 failure
}
declare function handleRequest(url: string, method: "GET" | "POST"): void;
const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);
大家覺得上面這段會不會出錯
答案是:「會!」
最後一行 handleRequest(req.url, req.method);
中的 req.method
會被推斷為 string
而不是字面量 GET
有兩種解法可以將型別轉換為 Literal Types
Type Assertion(型別推斷)
as
語法來明確指定特定型別req.method
的型別始終都是字面量 GET
// 1-1
const req = { url: "https://example.com", method: "GET" as "GET" };
// 1-2
handleRequest(req.url, req.method as "GET");
as const
(Const Assertion)as const
稱為「常數斷言」,它用來修改型別推斷的結果,確保所有屬性都被指派為 Literal Types,而不是其他型別,例如:string, numberimmutable
),視為常數const req = { url: "https://example.com", method: "GET" as const };
Template Literal Types
是 TypeScript 4.1 之後的功能
基於 String Literal Types
的延伸,並且能夠透過「聯合(unions)」來組合出更多字串,增加彈性
想了解聯合型別和交集型別可以看 Day12 - 聯合型別 ( | ) 、交集型別 ( & )
const v1 = 'TypeScript';
const v2 = `hello ${v1}`;
: hello TypeScript
就是 Template Literal Types
這樣看起來好像有點雞肋對吧...?
其實 Template Literal Types 真正威力在於能夠與「聯合型別(Union Type)」結合,來動態產生字串,
建立高度客製化的型別
以下範例參考官方文件
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
AllLocaleIDs 會被推論為下圖結果
再加上下面這段看看
type Lang = "en" | "ja" | "pt";
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
LocaleMessageIDs
很像細胞繁殖,繁殖到不知道誰是誰了😂
Literal Types
是用來「限定變數或參數的型別必須是字面上的值」Literal Types
的表示方式,可將其視為「眾多表示方式的集合體」Template Literal Types
是基於 String Literal Types
的延伸,並且能夠透過「聯合擴展(unions)」來組合出更多字串,增加更多彈性每天講的內容有推到 github 上喔