TypeScript 筆記 — Utility Types 與小技巧整理
前言
撰寫這份筆記是為了避免日後遺忘各種 TypeScript 技巧,方便隨時回顧。Utility Types 是 TypeScript 內建的型別工具,能在不重複撰寫型別的前提下重組、過濾或推斷型別,讓程式碼更精簡也更安全。
1. Pick<Type, Keys> — 取子集
從
Type中 挑出Keys指定的屬性,形成新型別。
- 使用時機:API 僅需輸出部分欄位、或在表單中僅編輯特定欄位時。
2. Omit<Type, Keys> — 去子集
與
Pick相反,排除Keys指定的屬性。
- 使用時機:過濾掉敏感或不必要的欄位後再傳遞。
3. Extract<Type, Union> — 取交集
從
Union中篩選出同時存在於Type的成員。
4. Exclude<Type, Union> — 去交集
從
Type中剔除與Union重疊的成員。
5. ReturnType<Fn> — 推斷回傳型別
自動取得函式
Fn的回傳型別,避免 hard code。
6. Record<Keys, Type> — 建立 Map 型別
以
Keys為索引、Type為值,快速建立物件型別。
與 {}/{ [key: string]: any } 的差異
| 寫法 | 目的 | 約束力 | |
|---|---|---|---|
Record<'a' | 'b', number> | 僅允許特定鍵且值型別一致 | 最嚴格 | |
{} | 任意屬性可加入 | 最寬鬆 | |
{ [key: string]: any } | 鍵為任意字串、值任意 | 中等(鍵不限,但無法限制值) |
實務建議:需要「列舉型」索引時使用
Record,其餘場景視需求使用一般物件或自訂索引簽章。
7. 讓複雜型別更易閱讀的 Prettify<T>
當型別交叉 (&) 或條件推導過多時,Hover 時顯示內容常難以閱讀。以下 Prettify<T> 可達到型別展開的笑過,僅保留最終展開後的型別結構。
小提醒:
& {}觸發 type flattener,讓 IDE 顯示「展開後」的結果。
8. satisfies 運算子 — 類型驗證但保留 literal
TS 4.9 加入的
satisfies可在保持具體值型別的同時,檢查物件是否符合某型別。
這個例子告訴我們 satisfies 的一個好處:它可以檢查物件是否符合指定的型別,還能保留精確的 literal type。
如果沒有 satisfies,我們通常會使用 as const 或加上額外的型別註記,但那樣寫起來就有點繁瑣,不太直覺。有了 satisfies,程式碼變得更簡單,型別推斷也更準確喔!
Type Annotation versus Satisfies
如果是使用 Type Annotation
在這裡,cat.amount 被限制成 string | number,雖然實際值是數字,但 TypeScript 不會自動幫我們限縮成 number。這種寫法有時會損失掉更精確的型別資訊。
但如果是使用 satisfies
這裡就是 satisfies 的第二個優點:在驗證物件符合指定型別的同時,保留了推斷出來的實際型別。因此 cat.weight 仍然被推斷為 32 (一個 number),而不是放寬成 string | number。
補充:as const vs. Object.freeze()
| 特性 | as const | Object.freeze() |
|---|---|---|
| 作用時間 | 編譯期 | 執行期 |
鍵是否變 readonly | ✅ | ✅ (淺層) |
| 值是否變為 literal type | ✅ | ❌ (仍為 type widening) |
| 回傳值 | 原值(type narrowing) | 同物件( freeze 後再回傳) |
結論:要保留 literal type請用
as const,要防止執行期誤改再配合Object.freeze()。