「TypeScript 在你執行程式之前就告訴你哪裡錯了—— 而不是讓你在生產環境的 3 點鐘接到警報才發現。」
本系列所有的 Next.js 程式碼都是用 TypeScript 寫的,但我們一直沒有正面介紹它。 這篇把欠的帳還清——從型別系統的設計哲學,到你在 React 開發中最常用到的 TS 特性。
為什麼要用 TypeScript?
🔍
編譯期錯誤發現
打字錯誤、錯誤的 API 呼叫、undefined 存取,在執行前就被抓到。
💡
IDE 自動補全
知道型別,IDE 就能提示可用的屬性和方法,大幅提升開發速度。
📖
程式碼即文件
函數的參數型別和回傳型別本身就是最準確的文件,不需要另外維護。
⚠️ TypeScript 只保護你到「編譯期」
TypeScript 在 tsc 編譯時做型別檢查,編譯後輸出的是純 JavaScript。執行期的資料(API 回應、使用者輸入、localStorage)不受型別保護——你的型別定義只是「你跟編譯器說的話」,不是執行時的驗證。 這就是為什麼處理外部資料時,還需要 Zod 這類 runtime validation 工具。
基礎型別與型別推斷
unknown + Type Narrowing:比 any 更安全的做法
收到 unknown 型別後,必須先用 Type Guard 縮小型別範圍(Narrowing),才能存取屬性。 這讓你在處理外部資料時保持型別安全。
Interface vs Type:怎麼選?
這是 TypeScript 最常見的問題。兩者功能很相近,但有幾個關鍵差異。
在 React + Next.js 中的慣例
組件的 Props 通常用 interface(因為語義上代表「這個組件接受什麼形狀的輸入」), API 回應、Redux state、資料模型通常用 type。 最重要的是整個專案保持一致——不要一半用 Interface 一半用 Type。
泛型(Generics):可重複使用的型別
泛型讓你寫出「型別安全的通用程式碼」——邏輯相同但適用於不同型別的情況。 它是 TypeScript 最強大的特性,也是最多人跳過的部分。
TypeScript 在 React 中的實戰
學完泛型後,React 的 TypeScript 寫法其實很直覺:useState<User | null>(null)、useRef<HTMLInputElement>(null)—— 這些都是泛型的直接應用,TypeScript 幫你確保 state 的型別和元件 Props 的形狀, 編譯期就能抓到 API 回傳值型別錯誤或 Props 傳錯型別的問題。
tsconfig.json 嚴格模式:你應該開啟的選項
最重要的:strictNullChecks
常見陷阱與最佳實踐
⚠️ 陷阱:過度使用 any
❌ 常見寫法
const data: any = fetchSomething(); // 型別檢查被關閉
✅ 更好的做法
const data: unknown = fetchSomething();
if (typeof data === 'object' && data !== null) { /* safe */ }💡 any 讓 TypeScript 沉默,但不能讓錯誤消失。用 unknown 強制自己做型別檢查。
⚠️ 陷阱:型別斷言(as)的濫用
❌ 常見寫法
const user = data as User; // 強制告訴 TS「相信我」,但可能錯
✅ 更好的做法
// 用型別守衛(Type Guard)更安全
function isUser(obj: unknown): obj is User {
return typeof obj === 'object' && obj !== null && 'id' in obj;
}💡 型別斷言是「逃生艙」,不是正常手段。寧可用型別守衛。
⚠️ 陷阱:忽略 API 回應的型別
❌ 常見寫法
const res = await fetch('/api/user');
const user = await res.json(); // user 是 any!✅ 更好的做法
const res = await fetch('/api/user');
const user = await res.json() as User; // 至少斷言
// 更好:用 zod 或 superstruct 做 runtime 驗證💡 fetch 的 .json() 永遠回傳 any。考慮用 Zod 做 schema validation。
進階:用 Zod 做 Runtime 型別驗證
TypeScript 型別在執行期消失,API 回應可能不符合你定義的型別。Zod 讓你同時得到「型別推斷」和「執行期驗證」:
本篇重點回顧
上一篇
EP.13 — Docker 容器化
VM vs Container / Dockerfile / Compose
下一篇
EP.15 — RESTful API 設計
URL、HTTP Method、狀態碼的藝術