iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

昨天我們聊了函式型別,今天要升級一下,

進入 TypeScript 型別系統中一個超強的功能——泛型(Generics)

如果你覺得型別一旦定義就很死板,那泛型會讓你眼睛一亮:

它讓型別變成「變數」一樣,可以傳進去再決定。


1. 為什麼需要泛型?

先看一個不用泛型的情況:

function identityString(value: string): string {
  return value;
}

function identityNumber(value: number): number {
  return value;
}

如果我們想讓這個函式能處理任何型別,只能寫一堆重複的版本,超麻煩。

這時候泛型就能出場。


2. 泛型的基本語法

function identity<T>(value: T): T {
  return value;
}

identity<string>("Hello");
identity<number>(123);
  • <T>:泛型參數(可以取任何名字,但 T 很常見)
  • 呼叫時 <string> 表示這次的 T 是 string
  • 回傳型別會跟參數型別一致

3. 型別推論 + 泛型

其實不用每次都手動寫 <T>,TS 會自己推:

let str = identity("Hello"); // T = string
let num = identity(123);     // T = number

4. 泛型在陣列中的應用

function getFirst<T>(arr: T[]): T {
  return arr[0];
}

let firstNum = getFirst([1, 2, 3]); // 推論 T = number
let firstStr = getFirst(["a", "b"]); // 推論 T = string

5. 泛型的約束(Constraints)

有時候我們不想讓 T 隨便是什麼型別,可以用 extends 約束它:

function getLength<T extends { length: number }>(item: T): number {
  return item.length;
}

getLength("Hello");  // OK
getLength([1, 2, 3]); // OK
getLength(123);       // ❌ Number 沒有 length 屬性

6. 多個泛型參數

function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}

let merged = merge({ name: "Alice" }, { age: 25 });
// merged 型別推論為 { name: string } & { age: number }

7. 泛型在 Type Alias 和 Interface 裡

type ApiResponse<T> = {
  data: T;
  status: number;
};

interface Repository<T> {
  getAll(): T[];
  getById(id: string): T;
}

let userRepo: Repository<{ id: string; name: string }> = {
  getAll: () => [{ id: "u1", name: "Bob" }],
  getById: (id) => ({ id, name: "Bob" })
};

8. 實務案例:泛型 + Promise

function fetchData<T>(url: string): Promise<T> {
  return fetch(url).then(res => res.json());
}

fetchData<{ id: string; name: string }>("/api/user")
  .then(user => console.log(user.name));

好處:

  • API 回傳資料型別自動檢查
  • 少掉手動轉型的麻煩

9. 常見踩坑

9.1 忘了加約束

function printLength<T>(item: T) {
  console.log(item.length); // ❌ Property 'length' does not exist on type 'T'
}

解法:

function printLength<T extends { length: number }>(item: T) {
  console.log(item.length);
}

9.2 過度使用泛型

不是每個地方都需要泛型,簡單型別直接用 stringnumber 反而更清楚。


上一篇
Day 10|函式型別:參數、回傳值全都要規範起來
下一篇
Day 12|泛型進階:預設值、多參數與更嚴格的約束
系列文
我與型別的 30 天約定:TypeScript 入坑實錄12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言