遷移指南
本遷移指南旨在按影響程度從高到低的順序列出 Zod 4 中的重大變更。要了解有關 Zod 4 的性能增強和新功能的更多信息,請閱讀 介紹文章。
Zod 的許多行為和 API 已變得更加直觀和有凝聚力。本文檔中描述的重大變更通常代表了 Zod 用戶的主要生活質量改善。我強烈建議仔細閱讀本指南。
注意 — Zod 3 導出了許多未記錄的準內部實用程序類型和函數,這些不被視為公共 API 的一部分。對這些內容的更改不在此處記錄。
非官方 codemod — 社區維護的 codemod zod-v3-to-v4 可用。
錯誤自定義 (Error customization)
Zod 4 將錯誤自定義的 API 標準化為一個單一的、統一的 error 參數。以前,Zod 的錯誤自定義 API 是碎片化和不一致的。這在 Zod 4 中得到了清理。
棄用 message
用 error 替換 message。message 參數仍然受支持,但已棄用。
移除 invalid_type_error 和 required_error
invalid_type_error / required_error 參數已被移除。這些是幾年前匆忙添加的,作為一種比 errorMap 更簡潔的自定義錯誤的方法。它們帶來了各種各樣的問題(它們不能與 errorMap 結合使用),並且與 Zod 的實際問題代碼不一致(沒有 required 問題代碼)。
這些現在可以用新的 error 參數清晰地表示。
移除 errorMap
這被重命名為 error。
錯誤映射現在也可以返回純 string(而不是 {message: string})。它們還可以返回 undefined,這告訴 Zod 將控制權交給鏈中的下一個錯誤映射。
ZodError
更新問題格式 (updates issue formats)
問題格式已大幅簡化。
以下是 Zod 3 問題列表及其 Zod 4 等效項:
雖然某些 Zod 4 問題類型已被合併、刪除和修改,但每個問題在結構上仍然與 Zod 3 對應項相似(在大多數情況下是相同的)。所有問題仍然符合與 Zod 3 相同的基本接口,因此大多數常見的錯誤處理邏輯將無需修改即可工作。
更改錯誤映射優先級 (changes error map precedence)
錯誤映射優先級已更改為更加一致。具體來說,傳遞給 .parse() 的錯誤映射 不再 優先於架構級別的錯誤映射。
棄用 .format()
ZodError 上的 .format() 方法已被棄用。相反,請使用頂級 z.treeifyError() 函數。有關更多信息,請閱讀 格式化錯誤文檔。
棄用 .flatten()
ZodError 上的 .flatten() 方法也被棄用。相反,請使用頂級 z.treeifyError() 函數。有關更多信息,請閱讀 格式化錯誤文檔。
移除 .formErrors
此 API 與 .flatten() 相同。它的存在是出於歷史原因,並沒有被記錄。
棄用 .addIssue() 和 .addIssues()
如果有必要,直接推入 err.issues 數組。
z.number()
no infinite values
POSITIVE_INFINITY 和 NEGATIVE_INFINITY 不再被視為 z.number() 的有效值。
.safe() no longer accepts floats
在 Zod 3 中,z.number().safe() 已被棄用。它現在的行為與 .int() 相同(見下文)。重要的是,這意味著它不再接受浮點數。
.int() accepts safe integers only
z.number().int() API 不再接受不安全整數(超出 Number.MIN_SAFE_INTEGER 和 Number.MAX_SAFE_INTEGER 範圍)。使用超出此範圍的整數會導致自發的捨入誤差。(另外:您應該切換到 z.int()。)
z.string() 更新
deprecates .email() etc
字串格式現在表示為 ZodString 的 子類,而不是簡單的內部細化。因此,這些 API 已移動到頂級 z 命名空間。頂級 API 也更簡潔,更易於 tree-shakable。
方法形式(z.string().email())仍然存在並且像以前一樣工作,但現在已被棄用。
stricter .uuid()
z.uuid() 現在更嚴格地根據 RFC 9562/4122 規範驗證 UUID;具體來說,根據規範,變體位必須是 10。對於更寬鬆的「類 UUID」驗證器,請使用 z.guid()。
no padding in .base64url()
z.base64url()(以前是 z.string().base64url())中不再允許填充。通常,希望 base64url 字串不填充且 URL 安全。
drops z.string().ip()
這已被單獨的 .ipv4() 和 .ipv6() 方法取代。如果需要同時接受兩者,請使用 z.union() 組合它們。
updates z.string().ipv6()
現在使用 new URL() 構造函數進行驗證,這比舊的正則表達式方法更健壯。以前通過驗證的一些無效值現在可能會失敗。
drops z.string().cidr()
同樣,這已被單獨的 .cidrv4() 和 .cidrv6() 方法取代。如果需要同時接受兩者,請使用 z.union() 組合它們。
z.coerce 更新
所有 z.coerce 架構的輸入類型現在都是 unknown。
.default() 更新
.default() 的應用方式發生了微妙的變化。如果輸入是 undefined,ZodDefault 會短路解析過程並返回默認值。默認值必須可以分配給 輸出類型。
在 Zod 3 中,.default() 期望一個與 輸入類型 匹配的值。ZodDefault 會解析默認值,而不是短路。因此,默認值必須可以分配給架構的 輸入類型。
為了複製舊的行為,Zod 實現了一個新的 .prefault() API。這是「預解析默認值」的縮寫。
z.object()
在可選字段中應用默認值
屬性內部的默認值會被應用,即使在可選字段中也是如此。這更符合預期,並解決了 Zod 3 中長期存在的可用性問題。這是一個微妙的變化,可能會導致依賴鍵存在的代碼路徑中斷等。
棄用 .strict() 和 .passthrough()
通常不再需要這些方法。相反,請使用頂級 z.strictObject() 和 z.looseObject() 函數。
這些方法對於向後兼容性仍然可用,並且不會被刪除。它們被視為遺留方法。
棄用 .strip()
這從來都不是特別有用,因為它是 z.object() 的默認行為。要將嚴格對象轉換為「常規」對象,請使用 z.object(A.shape)。
移除 .nonstrict()
這個長期棄用的 .strip() 別名已被刪除。
移除 .deepPartial()
這在 Zod 3 中早就被棄用了,現在在 Zod 4 中被刪除了。此 API 沒有直接的替代方案。它的實現中有很多陷阱,並且它的使用通常是一種反模式。
更改 z.unknown() 可選性
z.unknown() 和 z.any() 類型在推斷類型中不再標記為「鍵可選」。
棄用 .merge()
ZodObject 上的 .merge() 方法已被棄用,建議使用 .extend()。.extend() 方法提供相同的功能,避免了關於嚴格性繼承的歧義,並具有更好的 TypeScript 性能。
注意:為了獲得更好的 TypeScript 性能,請考慮使用物件解構而不是 .extend()。有關更多詳細信息,請參閱 API 文檔。
z.nativeEnum() 已棄用
z.nativeEnum() 函數現已棄用,建議僅使用 z.enum()。z.enum() API 已被重載以支持類似枚舉的輸入。
作為 ZodEnum 重構的一部分,許久棄用和多餘的功能已被刪除。這些都是相同的,只因歷史原因而存在。
z.array()
更改 .nonempty() 類型
這現在的行為與 z.array().min(1) 相同。推斷類型不會改變。
舊行為現在最好用 z.tuple() 和「rest」參數來表示。這更符合 TypeScript 的類型系統。
z.promise() 已棄用
很少有理由使用 z.promise()。如果你有一個可能是 Promise 的輸入,請在用 Zod 解析它之前 await 它。
如果您使用 z.promise 通過 z.function() 定義異步函數,那也不再需要了;請參閱下面的 ZodFunction 部分。
z.function()
z.function() 的結果不再是 Zod 架構。相反,它充當定義 Zod 驗證函數的獨立「函數工廠」。API 也發生了變化;您需要預先定義 input 和 output 架構,而不是使用 args() 和 .returns() 方法。
如果您迫切需要具有函數類型的 Zod 架構,請考慮 此解決方法。
添加 .implementAsync()
要定義異步函數,請使用 implementAsync() 而不是 implement()。
.refine()
忽略類型謂詞
在 Zod 3 中,將 類型謂詞 作為細化函數傳遞仍然可以縮小架構的類型。這沒有被記錄,但在一些問題中進行了討論。現在情況不再如此。
移除 ctx.path
Zod 的新解析架構不會急切地評估 path 數組。這是一個必要的更改,解鎖了 Zod 4 的巨大性能改進。
移除函數作為第二個參數
以下可怕的重載已被移除。
z.ostring() 等已移除
未記錄的便捷方法 z.ostring()、z.onumber() 等已被移除。這些是定義可選字串架構的簡寫方法。
z.literal()
移除 symbol 支持
Symbol 不被視為字面值,也不能簡單地與 === 進行比較。這是 Zod 3 中的一個疏忽。
靜態 .create() 工廠已移除
以前,所有 Zod 類都定義了一個靜態 .create() 方法。這些現在被實現為獨立的工廠函數。
z.record()
移除單參數用法
以前,z.record() 可以使用單個參數。這不再受支持。
改進枚舉支持
記錄變得更聰明了。在 Zod 3 中,將枚舉作為鍵架構傳遞給 z.record() 會導致部分類型
在 Zod 4 中,情況不再如此。推斷類型符合您的預期,並且 Zod 確保詳盡性;也就是說,它確保在解析期間所有枚舉鍵都存在於輸入中。
要複製具有可選鍵的舊行為,請使用 z.partialRecord():
z.intersection()
在合併衝突時拋出 Error
Zod 交集針對兩個架構解析輸入,然後嘗試合併結果。在 Zod 3 中,當結果無法合併時,Zod 會拋出一個帶有特殊 "invalid_intersection_types" 問題的 ZodError。
在 Zod 4 中,這將拋出一個常規 Error。無法合併的結果的存在表明架構存在結構問題:兩個不兼容類型的交集。因此,常規錯誤比驗證錯誤更合適。
內部變更 (Internal changes)
典型的 Zod 用戶可能會忽略這條線以下的所有內容。這些更改不會影響面向用戶的 z API。
這裡列出的內部更改太多了,但有些可能與(有意或無意)依賴某些實現細節的常規用戶有關。這些更改將對在 Zod 之上構建工具的庫作者特別感興趣。
更新泛型
幾個類的泛型結構發生了變化。也許最重要的是 ZodType 基類的變化:
第二個泛型 Def 已完全刪除。相反,基類現在只跟踪 Output 和 Input。雖然以前 Input 值默認為 Output,但現在默認為 unknown。這允許涉及 z.ZodType 的泛型函數在許多情況下表現得更直觀。
z.ZodTypeAny 的需求已被消除;只需使用 z.ZodType 即可。
添加 z.core
許多實用函數和類型已移動到新的 zod/v4/core 子包中,以方便 Zod 和 Zod Mini 之間的代碼共享。
為了方便起見,zod/v4/core 的內容也會在 z.core 命名空間下從 zod 和 zod/mini 重新導出。
有關核心子庫內容的更多信息,請參閱 Zod Core 文檔。
移動 ._def
._def 屬性現在已移動到 ._zod.def。所有內部 defs 的結構可能會發生變化;這與庫作者有關,但不會在此處全面記錄。
移除 ZodEffects
這不會影響面向用戶的 API,這是一個值得強調的內部更改。這是 Zod 如何處理 細化 的更大重組的一部分。
以前,細化和轉換都位於名為 ZodEffects 的包裝類中。這意味著將任一項添加到架構中都會將原始架構包裝在 ZodEffects 實例中。在 Zod 4 中,細化現在位於架構本身內部。更準確地說,每個架構都包含一個「檢查」數組;「檢查」的概念在 Zod 4 中是新的,它將細化的概念概括為包括潛在的副作用轉換,如 z.toLowerCase()。
這在 Zod Mini API 中尤為明顯,它嚴重依賴 .check() 方法將各種驗證組合在一起。
添加 ZodTransform
與此同時,轉換已移動到專用的 ZodTransform 類中。此架構類表示輸入轉換;事實上,您現在實際上可以定義獨立的轉換:
這主要與 ZodPipe 結合使用。.transform() 方法現在返回 ZodPipe 的實例。
移除 ZodPreprocess
與 .transform() 一樣,z.preprocess() 函數現在返回 ZodPipe 實例,而不是專用的 ZodPreprocess 實例。
移除 ZodBranded
品牌化現在通過直接修改推斷類型來處理,而不是專用的 ZodBranded 類。面向用戶的 API 保持不變。

