Zod 3から4への移行 — 変わった書き方の対応表
この章の目次開く
Zod 4の安定版が出たのは2025年7月です。それ以前に書かれた記事やStack Overflowの回答はすべてv3の書き方なので、そのまま貼り付けても動かなかったり、動くけれど非推奨だったりします。
この章は「検索で出てきたコードをv4に直す」ための対応表です。本章の内容はZod 4.4.3で実際に動かして確認しています。
まず自分のバージョンを確認する
npm ls zod4.x ならこの章の左側(v3)の書き方は避け、右側を使ってください。
対応表1: 文字列フォーマット
v4では、メールやURLの検証がトップレベル関数に移りました。
| v3 | v4 |
|---|---|
z.string().email() | z.email() |
z.string().url() | z.url() |
z.string().uuid() | z.uuid() |
z.string().datetime() | z.iso.datetime() |
z.string().ip() | z.ipv4() / z.ipv6() |
対応表2: エラーメッセージの指定
ここがもっとも危険な変更です。v3のオプションは、エラーにならず黙って無視されます。
| v3 | v4 | v4での挙動 |
|---|---|---|
{ required_error: '必須' } | { error: '必須' } | 無視される(英語のまま) |
{ invalid_type_error: '型が違う' } | { error: '型が違う' } | 無視される |
{ errorMap: () => ... } | { error: () => ... } | 無視される |
.min(1, 'メッセージ') | 変更なし | そのまま動く |
// v3の書き方。v4では警告も出ず、メッセージが英語のまま
z.string({ required_error: '必須です' }).safeParse(undefined).error.issues[0].message;
// 'Invalid input: expected string, received undefined'
// v4
z.string({ error: '必須です' }).safeParse(123).error.issues[0].message;
// '必須です'
対応表3: エラーの整形
v3ではメソッド、v4ではトップレベル関数が推奨です。
| v3 | v4 |
|---|---|
error.format() | z.treeifyError(error) |
error.flatten() | z.flattenError(error) |
| (なし) | z.prettifyError(error) |
.format() と .flatten() はメソッドとして4.4.3でも残っています。急いで書き換える必要はありませんが、新しく書くなら関数版を使ってください。
対応表4: オブジェクトの未知キー
| v3 | v4 |
|---|---|
z.object({...}).strict() | z.strictObject({...}) |
z.object({...}).passthrough() | z.looseObject({...}) |
.strip() | 既定の挙動(書く必要なし) |
.strict() / .passthrough() / .strip() はメソッドとして残っており、そのまま動きます。一方で .nonstrict() と .deepPartial() は削除されました。使っているコードはコンパイルエラーになるので、こちらは気づけます。
対応表5: その他
| 項目 | v3 | v4 |
|---|---|---|
| Record | z.record(値スキーマ) | z.record(キースキーマ, 値スキーマ) |
| 部分Record | — | z.partialRecord() が追加 |
| 文字列の真偽値 | 自作の transform | z.stringbool() が追加 |
| 軽量版 | — | zod/mini が追加 |
z.record(z.number()) のような1引数の書き方も4.4.3では動作しますが、v4ではキーのスキーマも明示する2引数の形が基本です。
段階的に移行する
Zod 4のパッケージにはv3の実装も同梱されています。サブパスから読み込めば、同じプロジェクト内で共存できます。
import { z } from 'zod'; // v4
import { z as z3 } from 'zod/v3'; // v3これが効くのは、依存ライブラリがまだv3にしか対応していない場合です。アプリのコードはv4で書きつつ、そのライブラリに渡すスキーマだけv3で作る、という分離ができます。大きなプロジェクトを一度に書き換えなくて済みます。
学習者そもそも、なんでこんなに変えたの?
先生大きいのはエラーメッセージの指定方法がバラバラだったこと。message / required_error / invalid_type_error / errorMap と4通りあったのを error 1つに寄せたんだ。他にも型推論の速度やバンドルサイズの改善が入っている。移行の手間はあるけど、方向としては素直な整理だよ。
移行のチェックリスト
古いコードをv4に持ってくるとき、上から順に確認してください。
npm ls zodで4系が入っているかrequired_error/invalid_type_error/errorMapが残っていないか(無言で壊れるので最優先)z.string().email()などをz.email()へ(急がなくてよい).nonstrict()/.deepPartial()を使っていないか(コンパイルエラーで気づける)error.format()/error.flatten()を関数版に寄せるか検討するz.record()を2引数の形にする
ちゃんと使うためのポイント
- 検索で出たコードが動かないときは、まずバージョンの違いを疑う
-
required_error/invalid_type_error/errorMapはv4では黙って無視される。最優先で潰す z.string().email()は動くが非推奨。z.string().ip()は削除済み.nonstrict()と.deepPartial()は削除。コンパイルエラーで気づけるzod/v3を併用すれば、段階的に移行できる
次の章では、ここまでで扱いきれなかった実務上の判断——スキーマの置き場所とハマりどころ をまとめます。
参考リンク
- Migration guide - Zod — v3からv4への変更点の公式一覧(英語)