iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0

今天我們要認識兩個超常用的型別——

Union(聯合型別)Intersection(交叉型別)

它們的概念超直覺,但用得好,型別設計會靈活到飛起來。


1. Union Type(聯合型別)

定義:允許變數是多種型別其中之一。

關鍵字:|(或)

let id: string | number;

id = "abc"; // OK
id = 123;   // OK
id = true;  // ❌ Type 'boolean' is not assignable to type 'string | number'

1.1 實務應用:函式參數

function printId(id: string | number) {
  console.log(`ID: ${id}`);
}
printId("abc");
printId(123);

1.2 型別守衛配合使用

當變數可能是多種型別時,要先做「型別守衛」才能安全使用。

function printUpperCase(value: string | string[]) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else {
    console.log(value.map(v => v.toUpperCase()));
  }
}

2. Intersection Type(交叉型別)

定義:將多個型別合併成一個型別,必須同時滿足所有型別條件。

關鍵字:&(且)

type Name = { name: string };
type Age = { age: number };

type Person = Name & Age;

let p: Person = { name: "Alice", age: 20 };

2.1 實務應用:合併功能

type CanWalk = { walk: () => void };
type CanSwim = { swim: () => void };

type SuperHero = CanWalk & CanSwim;

let hero: SuperHero = {
  walk: () => console.log("走路"),
  swim: () => console.log("游泳")
};

3. Union vs Intersection

| 特性 | Union (|) | Intersection (&) |

|------|------------|---------------------|

| 意義 | 其中之一 | 必須同時擁有 |

| 範例 | string | number | { name: string } & { age: number } |

| 使用場景 | 允許多種型別輸入 | 合併多個功能/資料結構 |


4. 實務案例:API 參數

假設有一個搜尋 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}`);
  }
}

5. 實務案例:混合功能的物件

有時候我們需要同時具備多種功能的物件,就用 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()
};

6. 常見陷阱

6.1 Union 不會自動展開

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();
}

6.2 Intersection 無法合併衝突型別

type A = { value: string };
type B = { value: number };

type AB = A & B;
// value 型別變成 never,因為 string & number 不可能同時成立

7. 延伸技巧:結合 Utility Types

type User = { name: string; email: string };
type Admin = User & { role: "admin" };
type Guest = User & { role: "guest" };

type AnyUser = Admin | Guest;

這樣能清楚描述角色型別,同時限制屬性內容。


上一篇
Day 7|型別推論:TS 比你想像的還聰明
下一篇
Day 9|Type Alias vs Interface:雙雄對決,該選誰?
系列文
我與型別的 30 天約定:TypeScript 入坑實錄12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言