Zod v4 使用指南:資料驗證核心功能
本文基於 Zod v4 版本撰寫。Zod 4 於 2025 年發布,帶來了顯著的效能提升、更小的 bundle size,以及多項新功能如內建 JSON Schema 轉換、遞迴物件支援等。
基本用法
定義 Schema
「Schema」是 Zod 的核心概念,它同時扮演兩個角色:執行期驗證器與編譯期型別定義。
傳統的 TypeScript 開發中,通常需要分別維護兩套定義:一套是 TypeScript 的 interface 或 type 用於編譯期型別檢查,另一套是執行期的驗證邏輯(如手寫的 if 判斷或使用其他驗證庫)。這種分離帶來同步問題——當資料結構改變時,必須同時更新兩個地方,否則就會產生型別定義與實際驗證邏輯不一致的 bug。
Zod 採用「單一來源」(Single Source of Truth)的設計理念來解決這個問題。只需要定義一次 schema,Zod 就會同時提供:
- 執行期驗證:在程式運行時檢查資料是否符合預期結構
- 編譯期型別:透過
z.infer自動推導出對應的 TypeScript 型別
修改 schema 時,型別定義會自動跟著改變,不需要手動同步,從根本上消除了型別與驗證邏輯不同步的風險。
import { z } from "zod";
const UserSchema = z.object({
username: z.string(),
age: z.number(),
});
解析資料 (parse)
使用 .parse() 驗證資料。驗證成功時會回傳一個經過驗證的深層複製資料,這確保了原始資料不會被意外修改,同時也讓 TypeScript 能夠正確推導出回傳值的型別。
const user = UserSchema.parse({ username: "john", age: 25 });
// => { username: "john", age: 25 }
// 驗證失敗會拋出 ZodError
UserSchema.parse({ username: 123, age: "25" }); // throws ZodError
安全解析 (safeParse)
相較於 .parse() 會拋出例外,.safeParse() 回傳一個 discriminated union,可以用 if/else 處理成功與失敗的情況,不需要 try/catch。在表單驗證或 API 輸入處理時,通常會選擇 .safeParse(),因為驗證失敗是預期中的情況,不應該用例外來處理。
const result = UserSchema.safeParse({ username: "john", age: 25 });
if (!result.success) {
console.log(result.error); // ZodError
} else {
console.log(result.data); // { username: string; age: number }
}
非同步解析
當 schema 包含非同步操作(如 async refinements 或 async transforms)時,必須使用非同步版本的解析方法。常見的應用場景包括:驗證 email 是否已被註冊、檢查使用者名稱是否可用等需要查詢資料庫的情況。
await UserSchema.parseAsync(data);
await UserSchema.safeParseAsync(data);
型別推導 (Type Inference)
Zod 最強大的特性之一是能夠從 schema 自動推導出 TypeScript 型別。只需要定義一次 schema,就能同時獲得執行期驗證和編譯期型別檢查。
const UserSchema = z.object({
username: z.string(),
age: z.number(),
});
// 使用 z.infer 提取型別
type User = z.infer<typeof UserSchema>;
// => { username: string; age: number }
// 現在可以在任何地方使用這個型別
const user: User = { username: "john", age: 25 };
當 schema 包含轉換邏輯時,輸入與輸出型別可能不同。Zod 提供了 z.input 和 z.output 來分別提取這兩種型別:
const schema = z.string().transform((val) => val.length);
type SchemaInput = z.input<typeof schema>; // string
type SchemaOutput = z.output<typeof schema>; // number (等同於 z.infer)
原始型別 (Primitives)
Zod 提供了對應 JavaScript 所有原始型別的 schema。這些是建構更複雜 schema 的基礎元件。
// 基本型別
z.string();
z.number();
z.bigint();
z.boolean();
z.date();
z.symbol();
// 空值型別
z.undefined();
z.null();
z.void(); // 接受 undefined
// 萬用型別
z.any();
z.unknown();
// Never 型別
z.never();
字面值 (Literals)
字面值 schema 用於驗證特定的固定值,常用於建構 discriminated union 或定義常數型別。
const tuna = z.literal("tuna");
const twelve = z.literal(12);
const isTrue = z.literal(true);
// 取得字面值
tuna.value; // "tuna"
字串驗證 (Strings)
字串是最常見的驗證對象。Zod 提供了豐富的內建驗證方法,涵蓋長度限制、格式驗證、內容檢查等常見需求。