🎯 什麼情境該想到我
當你猶豫「這個型別要從既有型別挖出來(Pick/keyof/typeof),還是老實再寫一份?」的時候。
⚙️ 怎麼用(步驟 / 公式)
先認識工具
| 語法 | 做什麼 |
|---|---|
keyof T | 把 T 的 key 抽成聯集 |
typeof v | 從值取出它的型別 |
T[K] | 索引存取:取出某個屬性的型別 |
Pick<T, K> / Omit<T, K> | 挑出/排除某些屬性 |
interface A extends B | 繼承 |
⭐ 判準:兩邊是不是同一個關注點?
該推導 —— 共用同一個關注點
const albumTypes = { CD: "cd", VINYL: "vinyl" } as const;
type AlbumType = (typeof albumTypes)[keyof typeof albumTypes];值和型別本來就是同一件事的兩面。解耦的話你要維護兩份會走鐘的來源。
該解耦 —— 關注點不同
// ❌ 誘人但耦合:UI 元件綁死資料層的型別
type AvatarImageProps = Pick<User, "imageUrl" | "name">;
// ✅ 各自寫清楚
type AvatarImageProps = { imageUrl: string; name: string };User 是資料型別、AvatarImage 是 UI 元件——責任不同、改變的理由也不同。用 Pick 之後,這個元件不只依賴 User 的形狀,還依賴它存在於 db.ts 這件事。
⚠️ 書點名的心理陷阱
Pick之所以誘人,是因為它用到比較進階的 TS 特性,讓你覺得自己很聰明。但通常,做簡單的事、讓型別保持解耦,才是比較聰明的做法。
判斷時先問自己:我選推導,是因為它真的比較好,還是因為它看起來比較厲害?
🧪 我實際套用的紀錄
- 2026-08-28:(待填)
⚠️ 注意 / 什麼時候不適用
- 推導是一種耦合,而且是單向的:
Shape改不動Triangle,但Triangle一改就會漣漪到Shape。 - 推導鏈會變得難管。從推導型別再推導出下一個,鏈一長就沒人知道改一處會炸到哪裡。
- 跨模組邊界時特別要想清楚:同一個模組內推導通常沒問題;跨層(資料層 → UI 層)多半該解耦。
- 這條判準跟一般的模組設計一致——依耦合方向決定,不是依「哪個寫起來比較短」。
🔗 相關工具
- 工具-封裝變化點 —— 同一個判斷的另一面:哪些東西會一起改,就該放在一起
- 工具-針對介面編程 —— 解耦的時候,你其實是在替消費端定義它自己的最小介面
- 工具-as-const與推論寬窄 —— 「值與型別同源」是該推導的典型案例