Zod 4リリース
(zod.dev)- Zod 4が1年間の開発を経て安定版として公開され、Zod 3の設計上の限界を修正し、性能・バンドルサイズ・TypeScriptコンパイル効率を大幅に改善
- 2021年5月に登場したZod 3は、GitHubスター2,700・週間ダウンロード60万から、現在は37.8kスター・週間3,100万ダウンロード規模へ成長し、24個のマイナーバージョンを経て主要な改善には破壊的変更が必要に
- ベンチマークでは文字列パースが14倍、配列パースが7倍、オブジェクトパースが6.5倍高速化し、
tscの型インスタンス化も簡単な例で25,000回超から約175回へ減少 - コアバンドルサイズはgzip基準でZod 3の
12.47kbからZod 4通常版の5.36kbへ縮小し、ツリーシェイキング可能な関数型APIであるZod Miniは1.88kbまで小型化 - 新機能にはJSON Schema変換、メタデータレジストリ、再帰オブジェクト型推論、国際化、エラーのpretty-printing、テンプレートリテラル型、
z.stringbool()、統合されたerrorパラメータ、改善されたz.discriminatedUnion()などが含まれる
Zod 4が新しいメジャーバージョンになった理由
- Zod 4は1年間の活発な開発を経て安定版となり、より高速で小さく、
tsc効率の高い基盤の上に、長らく要望されていた機能を実装 - Zod v3.0は2021年5月にリリースされ、当時のGitHubスターは2,700、週間ダウンロードは60万だった
- 現在ZodはGitHubスター37.8k、週間ダウンロード3,100万を記録
- ベータ版が出た6週間前は、週間ダウンロードが2,300万だった
- 24個のマイナーバージョンが積み重なる中でZod 3のコードベースは限界に達し、最も要望の多かった機能と改善には破壊的変更が必要だった
- Zod 4は最も多くの推薦を受けた公開Issue 10件のうち9件をクローズ
パース性能とTypeScriptコンパイル効率
- Zod 4はリポジトリで直接実行できるベンチマークを提供
- パース性能の改善:
- 文字列パース: 14倍高速化
- 配列パース: 7倍高速化
- オブジェクトパース: 6.5倍高速化
- オブジェクトパースのベンチマークではMoltar validation library benchmarkを実行
tsc --extendedDiagnostics基準の簡単なファイルのコンパイルで、型インスタンス化が大幅に減少"zod/v3"使用時は25,000回超"zod/v4"使用時は約175回
- Zod 4は
ZodObjectと他のスキーマクラスのジェネリクスを再設計・単純化し、インスタンス化の急増を回避 .extend()と.omit()を繰り返し連結するパターンは、Zod 3ではコンパイルに4000msかかり、追加の.extend()呼び出しで"Possibly infinite"エラーを引き起こす可能性があった- 同じパターンがZod 4では
400msでコンパイルされ、10倍高速化 - 今後
tsgoコンパイラと併用すれば、Zod 4のエディタ性能はより大きなスキーマやコードベースまでスケールできる可能性がある
バンドルサイズとZod Mini
- 単純な
z.boolean()検証スクリプトをrollupでバンドルし、コアバンドルサイズを比較 - gzip基準のコアバンドルサイズ:
- Zod 3:
12.47kb - Zod 4通常版:
5.36kb
- Zod 3:
- Zod 4通常版のコアバンドルはZod 3より約57%小さく、2.3倍削減
- Zodのメソッド中心APIは根本的にツリーシェイキングが難しく、単純な
z.boolean()スクリプトでも.optional()、.array()のような未使用メソッドの実装を一緒に取り込む - Zod Miniは
zodと1:1で対応する、関数型でツリーシェイキング可能なAPIを提供- Zodがメソッドを使う箇所で、Zod Miniは通常ラッパー関数を使用
- パースメソッドはZodとZod Miniで同一
- 汎用
.check()メソッドでrefinementを追加
- 通常のZodは大半のユースケースで引き続き推奨され、例外的に厳しいバンドルサイズ制約があるプロジェクトではZod Miniを検討できる
zod/mini使用時のgzipバンドルサイズは1.88kb- Zod 3比で85%、6.6倍削減
- 比較表: Zod 3
12.47kb、Zod 4通常版5.36kb、Zod 4 Mini1.88kb
- 詳細は
zod/miniドキュメントで確認できる
メタデータとJSON Schema
- Zod 4はスキーマに強く型付けされたメタデータを追加する新しいシステムを導入
- メタデータはスキーマ自体ではなく、スキーマとtyped metadataを結び付けるschema registryに保存される
z.registry()でレジストリを作成でき、スキーマの.register()メソッドによる便利な登録も可能- Zodは共通のJSON Schema互換メタデータを受け取るグローバルレジストリ
z.globalRegistryをエクスポート .meta()メソッドはスキーマをz.globalRegistryへ手軽に追加する- Zod 3との互換性のため
.describe()は引き続き提供されるが、Zod 4では.meta()が推奨される - Zod 4は
z.toJSONSchema()によるファーストパーティのJSON Schema変換を提供 z.globalRegistryのメタデータはJSON Schema出力に自動的に含まれる- 生成されるJSON Schemaのカスタマイズ情報はJSON Schema docsにある
新しいスキーマ表現と型機能
- Zod 4は再帰オブジェクト型を適切に推論できる
- 再帰型と相互再帰型を表現できる
- Zod 3の再帰型パターンと異なり、型キャストが不要
- 結果のスキーマは通常の
ZodObjectインスタンスであり、すべてのメソッドを使用できる
Fileインスタンス検証用のFile schemaが追加z.templateLiteral()はTypeScriptのテンプレートリテラル型を表現- 文字列化可能なZodスキーマ型は内部正規表現を保存
- 対象には文字列、
z.email()のような文字列フォーマット、数値、boolean、bigint、enum、literal、undefined/optional、null/nullable、別のtemplate literalが含まれる z.templateLiteralコンストラクタはこれらを1つのsuper-regexにつなぎ合わせるz.email()のような文字列フォーマットは正しく強制されるが、custom refinementは強制されない- 詳細はtemplate literal docsにある
- 固定幅整数と浮動小数点型を表現する新しい数値フォーマットが追加
- 適切なinclusive minimum/maximum制約がすでに追加された
ZodNumberインスタンスを返す
- 適切なinclusive minimum/maximum制約がすでに追加された
- JavaScriptの
numberで安全に表現できる範囲を超えるbigint数値フォーマットも追加- 適切なinclusive minimum/maximum制約がすでに追加された
ZodBigIntインスタンスを返す
- 適切なinclusive minimum/maximum制約がすでに追加された
z.literal()は複数の値を選択的に受け取れるようになった
エラー、国際化、文字列フォーマット
- Zod 4はエラーメッセージを複数言語へグローバルに翻訳する新しい**
localesAPI**を導入 - 対応ロケールの完全な一覧はCustomizing errorsにあり、対応言語が追加されるたびに更新される
- Zodは
ZodErrorをユーザーフレンドリーな形式の文字列へ変換するトップレベル関数z.prettifyErrorを実装- 現在のフォーマットは設定できない
- 今後変更される可能性がある
- 既存の
zod-validation-errorを使用中なら引き続き利用できる
- emailなどすべての文字列フォーマットは
zモジュールのトップレベル関数へ昇格- より簡潔でツリーシェイキングに有利
z.string().email()のようなメソッド対応APIは引き続き利用できるがdeprecated状態- 次のmajorバージョンで削除予定
z.email()APIはcustom regular expressionをサポート- canonical email regexは1つに定まっておらず、アプリケーションごとにより厳格または緩い基準を選べる
- Zodは利便性のため、一般的な正規表現をいくつかエクスポート
boolean変換とエラーカスタマイズの単純化
- 既存の
z.coerce.boolean()はfalsy値をfalse、truthy値をtrueに変換する単純なAPIfalse、undefined、null、0、""、NaNなどはfalseになる- この動作は他の
z.coerceAPIと一致
- Zod 4はenv風のより精巧なboolean coercionのために**
z.stringbool()**を導入- truthy値とfalsy値をカスタマイズできる
- 詳細は
z.stringbool()docsにある
- Zod 4の破壊的変更の大半はエラーカスタマイズAPIに関するもの
- エラーカスタマイズは単一の
errorパラメータへ統合messageはerrorに置き換えられるmessageパラメータは引き続きサポートされるがdeprecated状態invalid_type_errorとrequired_errorは関数構文のerrorに置き換えられるerrorMapも関数構文のerrorに置き換えられる
合成性、refinement、変換
z.discriminatedUnion()は従来サポートしていなかった複数のスキーマ型をサポート- unionとpipeが含まれる
- discriminated unionを別のdiscriminated unionのメンバーとして使えるようになり、合成可能になった
- Zod 3ではrefinementが元のスキーマをラップする
ZodEffectsクラスに保存されていた- この構造のため、
.refine()を.min()のような他のスキーマメソッドと組み合わせて使いにくかった
- この構造のため、
- Zod 4ではrefinementがスキーマ内部に保存され、
.refine()と他のスキーマメソッドを自然に併用できる - 新しい
.overwrite()メソッドはinferred typeを変えないtransformを表現.transform()は出力型がランタイムでintrospectableではなく、transform関数が何でも返せるブラックボックス- そのためJSON Schemaへ安全に変換する方法がない
.overwrite()は元のクラスのインスタンスを返す- overwrite関数はrefinementとして保存され、inferred typeを変更しない
- 既存の
.trim()、.toLowerCase()、.toUpperCase()は.overwrite()を使って再実装
ライブラリ作者向けの基盤
- Zod Miniの追加により、ZodとZod Miniが共有するコア機能を含む
zod/v4/coreサブパッケージが作成された zod/v4/coreはZodを単なるライブラリから、他のライブラリに組み込める高速な検証基盤へ拡張- スキーマライブラリを作る場合は、ZodとZod Miniの実装を参考にして
zod/v4/core上に構築できる - ライブラリ作者向けにFor library authorsガイドが提供される
- Zod上に構築する際のbest practiceを扱う
- Zod 3、Zod 4、Miniを同時にサポートする方法に関するよくある質問に答える
1件のコメント
Hacker News の意見
作者です。何でも聞いてください。バージョン方針については、このアプローチの理由をかなり詳しくまとめてあります: https://github.com/colinhacks/zod/issues/4371
結局のところ、npm は Zod が置かれた状況をうまく扱えるようには設計されていません。数十〜数百のライブラリが Zod のインターフェース/クラスを直接取り込み、自分たちの公開 API で使っているため、Zod がメジャーバージョンを上げるたびに、それらのライブラリも新しいメジャーバージョンを出さなければなりません
単独で見れば合理的ですが、Zod では全員にとって苦痛なバージョンの雪崩が起きかねず、エコシステムのかなりの部分が v3 に永遠に固定されてしまいそうでした
そこで Go の方式に似せて、パッケージが新しい破壊的変更を含むバージョンを出す代わりに、新しいサブパスを追加する方式を選びました。TypeScript エコシステムでは、ライブラリが
zod@^3.25.0だけを peer dependency として持ち、"zod/v3"と"zod/v4"から必要なものを取り込むことで、両方のバージョンを同時にサポートできますただ、特殊な事情のためにやや変わったバージョン方針を取った理由は理解できますが、推移的依存関係での Zod バージョン衝突を心配する必要がないチームの立場からすると、
4.0.0パッケージもあってほしかったです理解が正しければ import を
"zod/v4"に変える必要がありますが、これはかなり騒がしい変更ですし、IDE が'zod'から自動 import してしまう問題を lint ルールで捕まえなければならず、面倒になり得ます.optional()を直す作業が上位 9〜10 件の issue 解決に含まれるのか気になります: https://github.com/colinhacks/zod/issues/635これが Zod で一番つらい点でしたが、それでも Zod がとても良いので我慢して使っています
これを Zod の範囲外と見ているのか、それともまだ実装する時間がなかっただけなのか気になります。関連する議論は https://github.com/colinhacks/zod/discussions/2134 にあります
3.25.0という意味なら、これはちょっと筋が通らないように聞こえますここ数か月 Zod のアルファ版を使ってきて、単に
package.jsonを直してアップグレードしたかったのですが、今では git 履歴全体を掘り返す必要がありそうですプロジェクトにはとても感謝していますが、このフィードバックに関しては必要以上に難しくしてしまったように思います。
3.xと並行して 4.x も配布できたのではないでしょうか「移行を簡単にするため、Zod 4 を
zod@3.25リリースの一部として Zod 3 と一緒に配布し、"/v4"サブパスから取り込ませる」という内容は、npm の依存関係管理が完全にめちゃくちゃだというシグナルに見えますpeer dependency があまりに壊れているため、v4 が v3 のふりをしなければならない状況になっています
npm が Zod の置かれた状況をうまく管理できていないという点はその通りです。ただし Zod には、ほとんどどんなライブラリも受け入れないという制約があります。数十〜数百のライブラリが
"zod"からインターフェース/クラスを直接取り込み、自分たちの公開 API で使っていますこうしたライブラリは、Zod がメジャーバージョンを上げるたびに新しいメジャーバージョンを出す必要があり、Zod ではそれがバージョンの雪崩を引き起こして全員にとって苦痛になり得ます。正直、エコシステムのかなりの部分が v3 に永遠に固定されてしまいそうでした
そこで Go に似せて、破壊的変更のあるリリースごとに新しいパッケージバージョンを出すのではなく、新しいサブパスを追加する方式を選びました。TypeScript エコシステムでは、
zod@^3.25.0という単一の peer dependency で"zod/v3"と"zod/v4"を同時にサポートできますつまり、使用箇所を一つずつ
import ... from 'zod/v4'にリファクタリングし、終わったら完全に v4 に上げる流れですむしろ npm は、必要なら複数バージョンを同時に実行したり、推移的依存関係グラフの一部を選択的に上書きしたりできるので、逃げ道が多いほうです
どのパッケージ管理システムがこの問題を解決しているのでしょうか? Maven のような「安定した」プラットフォームでも、Apache Commons が v2 から v3 に移るときのように、新しい名前空間でバージョンを出してこうした問題に対処しています
以前から気になっていた疑問がある。Zod がこれに合うかもしれないと聞いたが、ドキュメントを見てもどうすればいいのかよく分からない。
サーバー API が表現できるものよりも、より精巧な型を返すとしよう。たとえば
api/:postid/authorが User を返すが、通常の User の場合も匿名 User の場合もあり、usernameやlocationのようなフィールドが null で来ることがある。この場合、User オブジェクトを判別ユニオンとして表現したくなるかもしれない。別のエンドポイントから来るオブジェクトも型変換が必要になることがある。User が時には
Post[]を含み、Post がモデレーターの投稿なら特殊な属性を持つこともある。以前は
normalizeUser()やnormalizePost()のような関数を書いていたが、すぐに散らかってしまった。エンドポイントごとに User/Post モデルの異なる部分集合を返すので、normalizePostを5個ほど作る羽目になり、かなり雑然としている。こういう問題は普通どう解くのか気になっている。statusのような判別フィールドを基準にsuccessとfailedのオブジェクトを分けるような形。モデレーター用の特殊属性が多いが、全部列挙したり検査したりしたくないなら、パススルーの動作を定義できる。複数のメソッドが互いに異なるスキーマを持っていても共通部分があるなら、共有のオブジェクトスキーマを作り、そこに追加フィールドを載せたオブジェクトを作ればよい。可能性が多ければそれでも散らかるだろうが、すでに散らかっている状況なら、バリデーターは少なくともその散らかり具合を検証してくれる。
フルスタック TypeScript でないなら、たとえば Python バックエンドでは複数の
User形態を Pydantic モデルとユニオンで定義し、OpenAPI/GraphQL スキーマ生成とコード生成を通じて TypeScript クライアント型を作れる。"user_type"のような判別プロパティを追加すると、通常ケースと特殊ケースを扱いやすくなる。たとえば
user.user_type === 'authenticated'を検査すれば、型システムはその後にuser.nameを使えることを理解する。GraphQL クエリ用の TypeScript ライブラリは、選択したフィールドの形からレスポンスの形を動的に推論できる。
{ user { username posts { text } } }を選ぶとpostsのあるレスポンス型になり、{ user { username } }だけを選ぶとpostsプロパティは完全に消える。サーバーは自分が送るデータを分かっているのだから、その情報をメタデータとしてクライアントに提供すべき。実務的には、Python バックエンドが Pydantic スキーマを使い、それを基に OpenAPI 仕様を自動生成し、クライアントが型を生成する、といった形になり得る。
Zod はこれまで見てきた代替手段よりはるかに良いが、こうした明示的な検証が必要だという事実そのものが、現代の Web 開発がどこに来てしまったのかを示す失敗のように感じることがある。
フルスタック開発で同じ形を複数の方法で説明しなければならないのが、とてももどかしい。JS の入力検証、API 定義用の Swagger、サーバー側の入力検証、スキーマ適合性のための ORM、さらにサーバーとクライアントで別々の TypeScript 定義まで必要になって、うんざりする。
TypeScript は私たちが持つ最善の単一の真実の情報源のように感じられるが、実際にはリフレクションを試みるツールがそれぞれ自分のモデルとビルダーを再定義している。リフレクションが中核となる大きな領域だけで5つあり、それぞれに複数の実装がある。
以前の TypeScript 型出力ツールである
reflect-metadataはランタイム型情報を一部提供するが、非常に古いデコレーターとメタデータ仕様を対象としている: https://www.npmjs.com/package/reflect-metadataもしかすると統合の入り口に来ているのかもしれない。Standard Schema プロジェクトは主要な検証ライブラリの大半から支持されているようだ。ただし、これを API 定義や ORM ツールにまで広げるのはまだかなり初期段階。
https://github.com/standard-schema/standard-schema?tab=readm...
Zod スキーマで一度変更すれば、型検査とともにアプリ全体へ伝播する。Zod スキーマが単一の真実の情報源になるわけだ。
すでに JSON Schema/Swagger スキーマがあるなら、そこから Zod を生成すればよい。TypeScript ORM を使っているなら、ほぼ間違いなく Zod ジェネレーターがあるはず。
正直なところ、Zod はあまりに広く使われているので、自分にとってはスタック内の他のものが依存できる統合スキーマ定義になっている。型を直接定義するため、開発者が実際に最新状態を保つ。会社の Swagger ドキュメントは変更にいつも遅れる。
現状維持で問題ないのは、結果だけを求めて方法には関心のないクライアントや雇用主を相手にしている場合だけ。その文脈では、こうした不幸な複雑性の層も、少なくとも請求可能な時間を増やしてくれる。
もちろん、Zod が自分で選んだ TypeScript スキーマ定義および検証ライブラリである、という前提での話だ。
Zod 4 は良さそうだが、最新の改善を含めても ArkType は依然として一桁倍以上速い
後方互換性と構文互換性のために、完全に新しく始めたライブラリほど大幅に速くするのが難しい場合がある。最近のプロジェクトでこれらのツールをすべて分析し、この理由もあって ArkType を選んだ。TypeScript の使用感もより良く感じた
私たちは Zod をフォーム検証にしか使っていないので、「なぜこれが重要なのか?」と思ってしまう。人々が高スループットな API 入力メッセージのようなものを検証していて、性能がより重要になるケースがあるのだろうかと思う
それでも Zod から乗り換えることはなさそう
Zod チームの新リリースを祝福する。あまり否定的に聞こえるリスクを承知で言うと、マイグレーションガイドに書かれている 破壊的変更 の数を考えると身震いする
Zod に大きく依存しているプロジェクトなら、開発者の集中力と時間をかなり必要とする大変な作業になりそうだ。職場で 4〜5 年もののフロントエンドプロジェクトをいくつか保守してきたので特に共感する
大きな React プロジェクトはたいてい多くのライブラリに依存しており、それぞれのライブラリが大きな変更を出し、さらにドキュメントまで不足していると、あっという間に手に負えなくなる。JavaScript で仕事をするうえで最も嫌いな部分の一つで、すべてを合わせて滑らかに動き続けるようにする作業が、終わりのない上り坂のように感じられる
正直、悪夢だったし、むしろ Django で作っていればよかったと思った。予想に反して、Tailwind 3 → 4 の移行は最も苦痛なものの一つだった
miniエディションとともに 二重提供方式 を選んだのだ。パッケージマネージャーをいじらずに段階的な移行をしやすいminiエディションに関心のないユーザーはその部分を気にする必要はない。ただしminiエディションのツリーシェイキング上の利点が非常に大きく、代替開発を刺激していたため、Zod はこれを受け入れるか衰退する必要があった本番環境で数千人のユーザーを抱える SaaS を自分で運営しているフルスタック開発者として、SPA を作らず、JS フロントエンドフレームワークも使わないことにしたおかげで、自分がまったく知らずに済んでいる問題がどれほど多いかを、ときどき気持ちよく実感する
Zod/ArkType のようなものが必要だと考えたことすらなく、初めて聞いたし、使うこともない
SPA を作るのに必要な労力、複雑さ、ツールの多さには驚かされる。SPA を作らないと決めると、特定の問題群全体がそのまま存在しなくなる。その代わり、ブラウザを本来設計されたとおりに、ページ全体の再読み込みとバックエンド開発中心で使い、Laravel Livewire のようなバックエンド専用フレームワークで時々リアクティブ性を加える
アクセス制御から検証、状態管理まで、すべてがシンプルだ。アプリは高速で、レスポンシブで、現代的で、SEO に強く、本番環境で数千人に提供されている
結局どこかにはスキーマがある。データベースシステムに定義されているかもしれないし、ORM モデルに定義されているかもしれない。しかし大きなプロジェクトでデータベースシステムや ORM にスキーマを置くと非常に難しい問題が生じ得るので、私たちは Zod と Protobuf を使っている。Protobuf も Zod か似たようなもので完全に置き換えられると思う
英国 Post Office スキャンダルには Horizon という IT システムが関係している: https://en.wikipedia.org/wiki/British_Post_Office_scandal
詳細を読んでみると、Zod のような スキーマ検証 があれば、このスキャンダルを防ぐ一助になったのではないかと思う。集団訴訟の裁判所文書の技術付録でも、スキーマ使用の欠如が言及されている: https://en.wikipedia.org/wiki/Bates_%26_Others_v_Post_Office...
今の話だと、防御的に
if文を山ほど書いているように聞こえる。さらに悪い場合は、HTML クライアント側フォーム検証だけに依存しているのかもしれない専門家ではないので慎重に言いますが、JSON Schemaはスキーマベースなので、TypeScript 以外の言語でもバリデーターを実装でき、良い選択肢になり得ると思いました
https://ajv.js.org はそのような JSON Schema ライブラリの一つです。Zod と比べてどうなのか気になります
望むなら JSON 変換器としても使えます。たとえば文字列を受け取り、ISO 日付文字列かどうかを検証して
Dateオブジェクトを出力するスキーマを作成できます重要な違いは前処理と
refineです。Zod では検証前にコールバックを提供でき、これは非常に有用ですが JSON では表現できません。たとえば日付検証の前にMM/DD/YYYYをDD/MM/YYYYに変えることは、思ったより頻繁に役立ちます検証には AJV を使うか、TypeBox 自体のスキーマ検証システムを使えます。後者のほうがはるかに高速ですが同期式のみをサポートし、AJV にはデータベース確認のような非同期検証オプションがあります
この話題をめぐる否定的な反応には驚きました
Zod v4 の初期バージョンをテストし、新しい API は気に入りましたが、移行パスがどうなるのか非常に心配していました。いっそ新しいパッケージ名で配布しようと提案することも考えていました
しかし作者のアプローチは賢明です。すべての依存関係が更新されるのを待たなくても、私のような人が v4 をすぐ導入できるようにしてくれます
新しいプロジェクトに Zod を導入し始めたばかりでしたが、これ以上ないタイミングでした
今見えている内容からすると、v4 に移るには本当に多くの変更が必要だっただろうと思います