今天我們要認識兩個超常用的型別——
Union(聯合型別) 和 Intersection(交叉型別)。
它們的概念超直覺,但用得好,型別設計會靈活到飛起來。
定義:允許變數是多種型別其中之一。
關鍵字:|
(或)
let id: string | number;
id = "abc"; // OK
id = 123; // OK
id = true; // ❌ Type 'boolean' is not assignable to type 'string | number'
function printId(id: string | number) {
console.log(`ID: ${id}`);
}
printId("abc");
printId(123);
當變數可能是多種型別時,要先做「型別守衛」才能安全使用。
function printUpperCase(value: string | string[]) {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else {
console.log(value.map(v => v.toUpperCase()));
}
}
定義:將多個型別合併成一個型別,必須同時滿足所有型別條件。
關鍵字:&
(且)
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
let p: Person = { name: "Alice", age: 20 };
type CanWalk = { walk: () => void };
type CanSwim = { swim: () => void };
type SuperHero = CanWalk & CanSwim;
let hero: SuperHero = {
walk: () => console.log("走路"),
swim: () => console.log("游泳")
};
| 特性 | Union (|
) | Intersection (&
) |
|------|------------|---------------------|
| 意義 | 其中之一 | 必須同時擁有 |
| 範例 | string | number
| { name: string } & { age: number }
|
| 使用場景 | 允許多種型別輸入 | 合併多個功能/資料結構 |
假設有一個搜尋 API,可以依「關鍵字」或「日期範圍」搜尋。
type KeywordSearch = { keyword: string };
type DateSearch = { start: Date; end: Date };
type SearchParams = KeywordSearch | DateSearch;
function search(params: SearchParams) {
if ("keyword" in params) {
console.log(`搜尋關鍵字:${params.keyword}`);
} else {
console.log(`搜尋日期:${params.start} ~ ${params.end}`);
}
}
有時候我們需要同時具備多種功能的物件,就用 Intersection:
type Base = { id: string };
type Timestamped = { createdAt: Date; updatedAt: Date };
type Entity = Base & Timestamped;
let post: Entity = {
id: "p001",
createdAt: new Date(),
updatedAt: new Date()
};
type Animal = { name: string };
type Dog = Animal & { bark: () => void };
let pet: Animal | Dog = { name: "Buddy" };
pet.bark(); // ❌ Property 'bark' does not exist on type 'Animal | Dog'
要先判斷型別再用:
if ("bark" in pet) {
pet.bark();
}
type A = { value: string };
type B = { value: number };
type AB = A & B;
// value 型別變成 never,因為 string & number 不可能同時成立
type User = { name: string; email: string };
type Admin = User & { role: "admin" };
type Guest = User & { role: "guest" };
type AnyUser = Admin | Guest;
這樣能清楚描述角色型別,同時限制屬性內容。