發布說明
經過一年的積極開發:Zod 4 現已穩定!它更快、更輕量、更 tsc 高效,並實現了一些長期要求的功能。
非常感謝 Clerk,他們通過极其慷慨的 OSS Fellowship 支持了我對 Zod 4 的工作。在整個(比預期長得多的!)開發過程中,他們都是了不起的合作夥伴。
版本控制
升級:
有關重大變更的完整列表,請參閱 遷移指南。本文重點介紹新功能和增強功能。
為什麼要推出新的主要版本?
Zod v3.0 於 2021 年 5 月發布(!)。那時 Zod 在 GitHub 上有 2700 顆星,每週下載量為 60 萬次。今天,它擁有 3.78 萬顆星,每週下載量達到 3100 萬次(比 6 週前測試版發布時的 2300 萬次有所增加!)。在經歷了 24 個次要版本後,Zod 3 代碼庫已達到瓶頸;最常被要求的功能和改進需要進行重大變更。
Zod 4 一舉解決了 Zod 3 的許多長期存在的設計限制,為幾個長期要求的功能和性能的巨大飛躍鋪平了道路。它關閉了 Zod 10 個最高票數的未解決問題 中的 9 個。幸運的話,它將成為未來許多年新的基礎。
有關新功能的快速瀏覽,請參閱目錄。點擊任何項目即可跳轉到該部分。
基準測試
您可以在 Zod 存儲庫中親自運行這些基準測試:
然後運行特定的基準測試:
14 倍更快的字串解析
7 倍更快的陣列解析
6.5 倍更快的物件解析
這運行了 Moltar 驗證庫基準測試。
tsc 實例化減少 100 倍
考慮以下簡單的文件:
使用 "zod/v3" 和 tsc --extendedDiagnostics 編譯此文件會導致 >25000 次類型實例化。使用 "zod/v4" 僅導致約 175 次。
Zod 存儲庫包含一個 tsc 基準測試遊樂場。使用 packages/tsc 中的編譯器基準測試親自嘗試一下。具體數字可能會隨著實現的發展而變化。
更重要的是,Zod 4 重新設計並簡化了 ZodObject 和其他架構類的泛型,以避免一些有害的「實例化爆炸」。例如,重複鏈接 .extend() 和 .omit()——這在以前會導致編譯器問題:
在 Zod 3 中,這需要 4000ms 來編譯;並且添加對 .extend() 的額外調用會觸發「可能無限」錯誤。在 Zod 4 中,這在 400ms 內編譯完成,快了 10 倍。
結合即將推出的 tsgo 編譯器,Zod 4 的編輯器性能將擴展到更大的架構和代碼庫。
核心包大小減少 2 倍
考慮以下簡單的腳本。
就驗證而言,這大約是最簡單的了。這是有意為之的;這是衡量 核心包大小 的好方法——即使在簡單的情況下也會最終進入包的代碼。我們將使用 rollup 以及 Zod 3 和 Zod 4 捆綁它,並比較最終的包。
| Package | Bundle (gzip) |
|---|---|
| Zod 3 | 12.47kb |
| Zod 4 | 5.36kb |
Zod 4 的核心包小了約 57% (2.3 倍)。這很好!但我們可以做得更好。
介紹 Zod Mini
Zod 嚴重依賴方法的 API 從根本上來說很難進行 tree-shaking。即使我們簡單的 z.boolean() 腳本也會引入我們沒用到的一堆方法的實現,比如 .optional()、.array() 等。編寫更輕量的實現只能到此為止。這就是 Zod Mini 發揮作用的地方。
這是一個帶有功能性、可 tree-shaking API 的 Zod 變體,與 zod 一一對應。Zod 使用方法的地方,Zod Mini 通常使用包裝函數:
並非所有方法都消失了!Zod 和 Zod Mini 中的解析方法是相同的:
還有一個通用的 .check() 方法,用於添加細化(refinements)。
以下頂級細化在 Zod Mini 中可用。它們對應於哪些 Zod 方法應該是不言自明的。
這種更加函數式的 API 使打包器更容易對您未使用的 API 進行 tree-shake。雖然對於大多數用例仍然推薦使用常規 Zod,但任何對包大小有嚴格限制的項目都應考慮 Zod Mini。
核心包大小減少 6.6 倍
這是上面的腳本,更新為使用 "zod/mini" 而不是 "zod"。
當我們使用 rollup 構建此代碼時,gzip 壓縮後的包大小為 1.88kb。與 zod@3 相比,核心包大小減少了 85% (6.6 倍)。
| Package | Bundle (gzip) |
|---|---|
| Zod 3 | 12.47kb |
| Zod 4 (regular) | 5.36kb |
| Zod 4 (mini) | 1.88kb |
欲了解更多信息,請參閱專用的 zod/mini 文檔頁面。完整的 API 詳細信息混合在現有的文檔頁面中;只要 API 有分歧,代碼塊就會包含 "Zod" 和 "Zod Mini" 的單獨選項卡。
元數據 (Metadata)
Zod 4 引入了一個新系統,用於向您的架構添加強類型元數據。元數據不存儲在架構本身內部;相反,它存儲在將架構與某些類型化元數據相關聯的「架構註冊表」中。要使用 z.registry() 創建註冊表:
將架構添加到您的註冊表:
或者,為了方便起見,您可以在架構上使用 .register() 方法:
全局註冊表
Zod 還導出了一個全局註冊表 z.globalRegistry,它接受一些常見的兼容 JSON Schema 的元數據:
.meta()
為了方便地將架構添加到 z.globalRegistry,請使用 .meta() 方法。
為了與 Zod 3 兼容,.describe() 仍然可用,但首選 .meta()。
JSON Schema 轉換
Zod 4 通過 z.toJSONSchema() 引入了第一方 JSON Schema 轉換。
z.globalRegistry 中的任何元數據都會自動包含在 JSON Schema 輸出中。
有關自定義生成的 JSON Schema 的信息,請參閱 JSON Schema 文檔。
遞歸物件 (Recursive objects)
這是一個意想不到的功能。經過多年試圖解決這個問題,我終於 找到了一種方法 來在 Zod 中正確推斷遞歸物件類型。要定義遞歸類型:
您還可以表示 相互遞歸類型:
與 Zod 3 的遞歸類型模式不同,不需要類型轉換。結果架構是普通的 ZodObject 實例,並且可以使用全套方法。
檔案架構 (File schemas)
要驗證 File 實例:
國際化 (Internationalization)
Zod 4 引入了一個新的 locales API,用於將錯誤訊息全局翻譯成不同的語言。
請參閱 自定義錯誤 中的完整支持語言環境列表;此部分將隨著受支持語言的增加而隨時更新。
錯誤美化打印 (Error pretty-printing)
zod-validation-error 包的流行表明,對於美化打印錯誤的官方 API 有巨大的需求。如果您目前正在使用該包,請務必繼續使用它。
Zod 現在實現了一個頂級 z.prettifyError 函數,用於將 ZodError 轉換為用戶友好的格式化字串。
這將返回以下可美化打印的多行字串:
目前格式不可配置;這可能會在未來發生變化。
頂級字串格式 (Top-level string formats)
所有「字串格式」(電子郵件等)都已升級為 z 模組上的頂級函數。這既更簡潔又更易於 tree-shake。方法等效項(z.string().email() 等)仍然可用,但已被棄用。它們將在下一個主要版本中被移除。
自定義電子郵件正則表達式
z.email() API 現在支持自定義正則表達式。沒有一個標準的電子郵件正則表達式;不同的應用程序可能會選擇更嚴格或更寬鬆。為了方便起見,Zod 導出了一些常見的正則表達式。
模板字串類型 (Template literal types)
Zod 4 實現了 z.templateLiteral()。模板字串類型可能是 TypeScript 類型系統中以前無法表示的最大功能。
每個可以被字串化的 Zod 架構類型都存儲一個內部正則表達式:字串、字串格式如 z.email()、數字、布爾值、bigint、枚舉、字面量、undefined/optional、null/nullable 和其他模板字串。z.templateLiteral 構造函數將這些連接成一個超級正則表達式,因此像字串格式(z.email())這樣的東西會被正確強制執行(但自定義細化不會!)。
如需更多信息,請閱讀 模板字串文檔。
數字格式 (Number formats)
添加了新的數字「格式」用於表示固定寬度的整數和浮點類型。這些返回一個 ZodNumber 實例,其中已添加了適當的最小值/最大值約束。
同樣,也添加了以下 bigint 數字格式。這些整數類型超出了 JavaScript 中 number 可以安全表示的範圍,因此這些返回一個 ZodBigInt 實例,其中已添加了適當的最小值/最大值約束。
布爾字串 (Stringbool)
現有的 z.coerce.boolean() API 非常簡單:假值(false、undefined、null、0、""、NaN 等)變為 false,真值變為 true。
這仍然是一個很好的 API,其行為與其他 z.coerce API 一致。但是一些用戶要求更複雜的「環境變數風格」布爾值強制轉換。為了支持這一點,Zod 4 引入了 z.stringbool():
自定義真值和假值:
有關更多信息,請參閱 z.stringbool() 文檔。
簡化的錯誤自定義 (Simplified error customization)
Zod 4 中的大多數重大變更都涉及 錯誤自定義 API。在 Zod 3 中,它們有些混亂;Zod 4 使事情變得更加優雅,我認為值得在此強調。
長話短說,現在有一個統一的 error 參數用於自定義錯誤,替換了以下 APIs:
用 error 替換 message。(message 參數仍然受支持,但已棄用。)
用 error(函數語法)替換 invalid_type_error 和 required_error:
用 error(函數語法)替換 errorMap:
升級的 z.discriminatedUnion() (Upgraded z.discriminatedUnion())
判別聯合(Discriminated unions)現在支持许多以前不支持的架構類型,包括聯合和管道:
也許最重要的是,判別聯合現在可以 組合——您可以將一個判別聯合用作另一個判別聯合的成員。
z.literal() 中的多個值
z.literal() API 現在可選地支持多個值。
細化存在於架構內部 (Refinements live inside schemas)
在 Zod 3 中,它們存儲在包裝原始架構的 ZodEffects 類中。這很不方便,因為這意味著您無法將 .refine() 與其他架構方法(如 .min())交錯使用。
在 Zod 4 中,細化存儲在架構本身內部,因此上面的代碼可以按預期工作。
.overwrite()
.transform() 方法非常有用,但它有一個主要缺點:輸出類型在運行時不再是 可內省的。轉換函數是一個可以返回任何東西的黑盒。這意味著(除其他外)沒有可靠的方法可以將架構轉換為 JSON Schema。
Zod 4 引入了一個新的 .overwrite() 方法,用於表示 不改變推斷類型 的轉換。與 .transform() 不同,此方法返回原始類的實例。覆蓋函數存儲為細化,因此它不會(也不能)修改推斷類型。
現有的 .trim()、.toLowerCase() 和 .toUpperCase() 方法已使用 .overwrite() 重新實現。
可擴展的基礎:zod/v4/core (An extensible foundation: zod/v4/core)
雖然這對大多數 Zod 用戶來說並不重要,但值得強調。Zod Mini 的添加需要創建一個共享子包 zod/v4/core,其中包含 Zod 和 Zod Mini 之間共享的核心功能。
起初我很抗拒這一點,但現在我認為它是 Zod 4 最重要的功能之一。它讓 Zod 從一個簡單的庫升級為一個快速驗證「基底」,可以灑入其他庫中。
如果您正在構建架構庫,請參考 Zod 和 Zod Mini 的實現,了解如何在 zod/v4/core 提供的基礎上進行構建。如有幫助或反饋,請隨時在 GitHub 討論區或通過 X/Bluesky 聯繫。
總結 (Wrapping up)
我正計劃寫一系列額外的文章,解釋像 Zod Mini 這樣的主要功能背後的設計過程。隨著這些文章的發布,我將更新此部分。
對於庫作者,現在有一個專用的 給庫作者 指南,描述了在 Zod 之上構建的最佳實踐。它回答了有關如何同時支持 Zod 3 和 Zod 4(包括 Mini)的常見問題。
快樂解析!
— Colin McDonnell @colinhacks

