標題: Zod 4 是 TypeScript-first 的 schema 驗證函式庫的重寫版本,於 2025 年以穩定主要版本發布。四項變更直接影響到我的程式碼:字串格式移至頂層函式(使用
z.email()取代z.string().email())、四種錯誤選項合併為單一error參數、錯誤格式化移至獨立的輔助函式(z.flattenError、z.treeifyError、z.prettifyError),以及.strict()/.passthrough()改為z.strictObject()/z.looseObject()。已棄用的 Zod 3 API 仍可運作但會顯示警告,因此我採用漸進式遷移。
重點摘要
- Zod 4 是 TypeScript-first 的 schema 驗證函式庫的穩定主要版本,於 2025 年發布;需要 TypeScript 5.5 或更新版本。
- 字串格式現已改為頂層可 tree-shake 的函式 —
z.email()、z.uuid()、z.url()— 且z.string().email()已棄用但仍可使用。 - 單一
error參數取代 Zod 3 的message、invalid_type_error、required_error及errorMap。 - 錯誤格式化移至
z.flattenError(表單欄位)、z.treeifyError(巢狀結構)及z.prettifyError(人類可讀字串)。 zod/mini建置版透過函式式、可 tree-shake 的 API 提供相同驗證器;z.infer、.parse()及.safeParse()並未改變。
幾乎在每個專案中,我都會使用 Zod 來驗證邊界處不受信任的輸入 — 例如請求主體、表單資料、環境變數、API 回應。Zod 4 改變了足夠多的表面 API,導致機械式升級影響了少數檔案,因此我精確地記錄了哪些部分發生了變動。
Zod 4 究竟改變了什麼?
Zod 4 是 TypeScript-first 的 schema 驗證函式庫的全面重寫版本,於 2025 年以穩定主要版本發布。重點在於效能:Zod 團隊的發布說明指出,TypeScript 編譯器實例化大幅減少,且執行時解析速度更快,這對於 schema 類型主導型別檢查時間的大型程式碼庫來說特別重要。四項 API 變更直接影響到我的程式碼 — 字串格式移至頂層函式、四種錯誤選項合併為一、錯誤格式化移至獨立輔助函式,以及 .strict()/.passthrough() 改為 z.strictObject()/z.looseObject()。
為什麼 z.string().email() 變成 z.email()?
Zod 4 將字串格式提升為獨立的頂層函式 — z.email()、z.uuid()、z.url(),以及 z.iso 下的 ISO 輔助函式 — 而非鏈接在 z.string() 上的方法。鏈接形式仍可運作,但已棄用並會發出警告。原因是 tree-shaking:每種格式都是獨立的函式,因此僅驗證電子郵件的 bundle 不再需要包含其他所有格式的邏輯。
import * as z from "zod";
const User = z.object({
id: z.uuid(),
email: z.email(),
website: z.url().optional(),
age: z.number().int().min(18),
});
type User = z.infer<typeof User>;
Enter fullscreen mode Exit fullscreen mode
在 Zod 4 中如何設定自訂錯誤訊息?
Zod 4 以單一 error 參數取代四種獨立的錯誤選項。在 Zod 3 中,你可以傳遞 message、invalid_type_error、required_error 或完整的 errorMap。在 Zod 4 中,你只需傳遞一個 error:字串代表固定訊息,或接收 issue 並回傳訊息的函式,讓你能在同一個地方區分缺少值與錯誤型別。
// Zod 4: one error param — string or function
z.string({ error: "Name is required" });
z.string({
error: (issue) =>
issue.input === undefined ? "Name is required" : "Name must be text",
});
Enter fullscreen mode Exit fullscreen mode
如何將 ZodError 轉換為表單錯誤?
Zod 4 將錯誤格式化移至三個頂層輔助函式。z.flattenError(error) 會回傳 { formErrors, fieldErrors },可對應到表單的欄位層級訊息。z.treeifyError(error) 會回傳與 schema 形狀相符的巢狀物件。z.prettifyError(error) 會回傳供日誌使用的人類可讀多行字串。原始的 error.issues 陣列維持不變;error.format() 及 error.flatten() 已棄用,請改用這些輔助函式。
const result = User.safeParse(await request.json());
if (!result.success) {
const { fieldErrors } = z.flattenError(result.error);
return Response.json({ errors: fieldErrors }, { status: 400 });
}
const user = result.data; // fully typed as User
Enter fullscreen mode Exit fullscreen mode
何時應該使用 zod/mini 而非完整的 zod 套件?
當 bundle 大小很重要時(例如用戶端程式碼、edge functions、已發布的小工具),請使用 zod/mini;其他情況則使用完整的 zod 套件。zod/mini 透過函式式 API 提供相同驗證器:取代鏈接 .optional(),你需要使用 z.optional() 包裝,而 refinements 則使用 .check() 而非 .refine()。雖然較為冗長,但由於沒有方法鏈,未使用的程式碼可以被 tree-shake 移除。兩種建置版共用相同核心,因此執行時行為完全相同。
import * as z from "zod/mini";
const User = z.object({
email: z.email(),
name: z.optional(z.string()),
});
Enter fullscreen mode Exit fullscreen mode
| 考量點 | zod(完整版) | zod/mini |
|---|---|---|
| API 風格 | 鏈接方法(.optional()) |
函式包裝器(z.optional()) |
| Bundle 大小 | 較大 | 較小、可 tree-shake |
| 易用性 | 流暢、易讀 | 較為冗長 |
| 驗證器相同 | 是 | 是(相同核心) |
| 最適合用於 | 伺服器、一般程式碼 | Edge/用戶端、對大小敏感的 bundle |
如何從 Zod 3 遷移而不破壞一切?
採用漸進式遷移,因為 Zod 4 保留已棄用的 Zod 3 API(會顯示警告而非移除)。在過渡期間,Zod 將新版本以 zod/v4 匯入路徑發布(在 [email protected] 中),讓你可以在 [email protected] 發布前逐檔採用;舊版 API 仍可透過 zod/v3 存取。我的流程是:升級套件、執行型別檢查與測試,然後分批清除棄用警告 — 重新命名字串格式呼叫、將錯誤選項合併為 error,以及將 .strict()/.passthrough() 切換至新的物件函式。z.infer、.parse() 及 .safeParse() 並未改變,因此大多數 schema 仍可正常運作。
常見問答
Q: z.string().email() 在 Zod 4 中已被移除嗎?
A: 沒有。它仍可運作,但已棄用並會顯示警告。建議的形式是頂層的 z.email(),兩者驗證結果相同。
Q: 在 Zod 4 中,什麼取代了 errorMap 與 invalid_type_error?
A: 單一 error 參數 — 字串代表固定訊息,或接收 issue 並回傳條件式訊息的函式。
Q: 在 Zod 4 中如何取得表單的欄位層級錯誤?
A: 呼叫 z.flattenError(error);其 fieldErrors 會將每個 schema 鍵對應到其訊息。若為巢狀形狀,請使用 z.treeifyError。
Q: zod/mini 是獨立的函式庫嗎?
A: 不是。它是 Zod 4 的建置版,透過函式式、可 tree-shake 的 API 提供相同驗證器,以取得更小的 bundle。
Q: Zod 4 是否需要特定版本的 TypeScript?
A: 是的。Zod 4 需要 TypeScript 5.5 或更新版本。
Originally published on devya.dev. Also on eng-ahmed.com. Built by Devya Solutions.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.