本文へスキップ
ウェブエンジニア問題集
第15章

Zod 3から4への移行 — 変わった書き方の対応表

約8分
この章の目次開く

Zod 4の安定版が出たのは2025年7月です。それ以前に書かれた記事やStack Overflowの回答はすべてv3の書き方なので、そのまま貼り付けても動かなかったり、動くけれど非推奨だったりします。

この章は「検索で出てきたコードをv4に直す」ための対応表です。本章の内容はZod 4.4.3で実際に動かして確認しています。

まず自分のバージョンを確認する

npm ls zod
bash

4.x ならこの章の左側(v3)の書き方は避け、右側を使ってください。


対応表1: 文字列フォーマット

v4では、メールやURLの検証がトップレベル関数に移りました。

v3v4
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のオプションは、エラーにならず黙って無視されます。

v3v4v4での挙動
{ 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;
// '必須です'
ts
「日本語にしたはずのメッセージが英語のまま」という症状が出たら、まずこの3つのオプションが残っていないかを疑ってください。
慌てている人のイラスト

対応表3: エラーの整形

v3ではメソッド、v4ではトップレベル関数が推奨です。

v3v4
error.format()z.treeifyError(error)
error.flatten()z.flattenError(error)
(なし)z.prettifyError(error)

.format() と .flatten() はメソッドとして4.4.3でも残っています。急いで書き換える必要はありませんが、新しく書くなら関数版を使ってください。

対応表4: オブジェクトの未知キー

v3v4
z.object({...}).strict()z.strictObject({...})
z.object({...}).passthrough()z.looseObject({...})
.strip()既定の挙動(書く必要なし)

.strict() / .passthrough() / .strip() はメソッドとして残っており、そのまま動きます。一方で .nonstrict() と .deepPartial() は削除されました。使っているコードはコンパイルエラーになるので、こちらは気づけます。

対応表5: その他

項目v3v4
Recordz.record(値スキーマ)z.record(キースキーマ, 値スキーマ)
部分Record—z.partialRecord() が追加
文字列の真偽値自作の transformz.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
ts

これが効くのは、依存ライブラリがまだv3にしか対応していない場合です。アプリのコードはv4で書きつつ、そのライブラリに渡すスキーマだけv3で作る、という分離ができます。大きなプロジェクトを一度に書き換えなくて済みます。

学習者学習者

そもそも、なんでこんなに変えたの?

先生先生

大きいのはエラーメッセージの指定方法がバラバラだったこと。message / required_error / invalid_type_error / errorMap と4通りあったのを error 1つに寄せたんだ。他にも型推論の速度やバンドルサイズの改善が入っている。移行の手間はあるけど、方向としては素直な整理だよ。


移行のチェックリスト

古いコードをv4に持ってくるとき、上から順に確認してください。

  1. npm ls zod で4系が入っているか
  2. required_error / invalid_type_error / errorMap が残っていないか(無言で壊れるので最優先)
  3. z.string().email() などを z.email() へ(急がなくてよい)
  4. .nonstrict() / .deepPartial() を使っていないか(コンパイルエラーで気づける)
  5. error.format() / error.flatten() を関数版に寄せるか検討する
  6. z.record() を2引数の形にする

ちゃんと使うためのポイント

  • 検索で出たコードが動かないときは、まずバージョンの違いを疑う
  • required_error / invalid_type_error / errorMap はv4では黙って無視される。最優先で潰す
  • z.string().email() は動くが非推奨。z.string().ip() は削除済み
  • .nonstrict() と .deepPartial() は削除。コンパイルエラーで気づける
  • zod/v3 を併用すれば、段階的に移行できる

次の章では、ここまでで扱いきれなかった実務上の判断——スキーマの置き場所とハマりどころ をまとめます。

参考リンク

TypeScriptクイズに挑戦するこの章で学んだ型と検証の知識を、4択クイズでアウトプットして定着させよう