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

文字列・数値・真偽値のスキーマ — 使える検証メソッド一覧

約10分
この章の目次開く

ここからは実際にスキーマを書いていきます。まずはすべての土台になる、単体の値を検証するスキーマからです。

スキーマは「作る」と「検証する」が別々

z.string() を呼んだ時点では、まだ何も検証されません。返ってくるのは検証の設計図であるスキーマオブジェクトで、実際の検証は parse や safeParse を呼んだときに走ります。

const schema = z.string(); // この時点では何も起きない
schema.safeParse('hello'); // ここで初めて検証される
ts

そして、検証メソッドをつなぐと新しいスキーマが返ります。元のスキーマは書き換わりません。

const base = z.string();
const strict = base.min(5);
 
base.safeParse('ab'); // 成功 — baseは変わっていない
strict.safeParse('ab'); // 失敗 — 5文字以上が必要
ts

検証メソッドは元のスキーマを変更せず、条件を足した別のスキーマを返します。だから z.string().min(3).max(10) のようにつなげて書けますし、共通のスキーマを変数に置いて各所で条件を足す、という使い方もできます。

学習者学習者

つなげて書けるのは分かったけど、.min() は何を返してるの? 検証結果じゃないの?

先生先生

返ってくるのはスキーマだよ。検証結果が返るのは parse と safeParse だけ。ここを混同すると「.min() の戻り値をifで判定する」みたいなコードを書いてしまうから、最初に押さえておこう。


文字列スキーマ

構文: z.string()

戻り値: 文字列スキーマ。typeof v === 'string' であることを検証する

数値の 123 を渡すと失敗しますが、"123" は文字列なので通ります。空文字も通ることは デフォルト挙動の章のとおりです。

長さ・形を検証するメソッド

メソッド引数何を検証するか
.min(n, message?)n: 最小文字数n 文字以上か
.max(n, message?)n: 最大文字数n 文字以下か
.length(n, message?)n: 文字数ちょうど n 文字か
.regex(re, message?)re: 正規表現パターンに一致するか
.startsWith(s, message?)s: 文字列s で始まるか
.endsWith(s, message?)s: 文字列s で終わるか
.includes(s, message?)s: 文字列s を含むか
.nonempty(message?)なし1文字以上か(.min(1) と同じ)

戻り値: いずれも条件を追加した新しいスキーマ。第2引数の message を渡すと、失敗時のメッセージを差し替えられる

const password = z.string().min(8, '8文字以上で入力してください');
 
password.safeParse('short').error.issues[0].message;
// "8文字以上で入力してください"
ts

値を書き換えるメソッド

検証ではなく、値そのものを変換するメソッドもあります。

メソッド効果
.trim()前後の空白を削除する
.toLowerCase()小文字にする
.toUpperCase()大文字にする

戻り値: 変換を追加した新しいスキーマ。検証を通ると、変換後の値が data に入る

z.string().trim().parse('  hello  '); // "hello"
z.string().toLowerCase().parse('ABC'); // "abc"
ts

元の入力ではなく戻り値が変換済みである点に注意してください。この性質は次の章でもう一度扱います。

書く順番で結果が変わる

変換メソッドと検証メソッドは、書いた順に上から実行されます。順番を入れ替えると結果が変わります。

z.string().trim().min(1).safeParse('   ');
// { success: false } — trimして "" になってから長さを見るので弾ける
 
z.string().min(1).trim().safeParse('   ');
// { success: true, data: "" } — 3文字あるので通過し、そのあとtrimされて空文字になる
ts
後者は「検証を通ったのに中身が空文字」という最悪の結果になります。変換は検証より先に書く、と覚えてください。
順番を選んでいる人のイラスト

文字列フォーマットのスキーマ

メールアドレスやURLのような「形式が決まった文字列」には、専用のスキーマが用意されています。Zod 4ではトップレベルの関数として呼びます。

スキーマ検証する形式
z.email()メールアドレス
z.url()URL
z.uuid()UUID
z.iso.date()2026-01-01 形式の日付文字列
z.iso.datetime()2026-01-01T00:00:00Z 形式
z.iso.time()12:30:00 形式
z.ipv4() / z.ipv6()IPアドレス
z.jwt()JWT
z.base64()Base64文字列
z.nanoid() / z.ulid() / z.cuid2()各種ID形式

戻り値: 文字列スキーマ。.min() などの文字列メソッドをそのままつなげられる

const schema = z.email().max(255);
ts

数値スキーマ

構文: z.number()

戻り値: 数値スキーマ。NaN と Infinity は弾かれる

メソッド引数何を検証するか
.min(n) / .gte(n)n: 下限n 以上か
.max(n) / .lte(n)n: 上限n 以下か
.gt(n) / .lt(n)n: 境界値n より大きいか / 小さいか(n 自体は不可)
.int()なし整数か
.positive()なし0 より大きいか
.nonnegative()なし0 以上か
.negative() / .nonpositive()なし負の数か / 0 以下か
.multipleOf(n)n: 倍数の基準n の倍数か
.finite()なし有限の数値か

戻り値: いずれも条件を追加した新しいスキーマ

境界の扱いは間違えやすいところです。実際の結果で並べます。

書き方0 を渡すと
z.number().positive()✗ 失敗
z.number().nonnegative()通る
z.number().min(0)通る

その他の基本スキーマ

スキーマ通る値主な用途
z.boolean()true / falseフラグ。文字列の "true" は通らない
z.bigint()BigInt大きな整数
z.date()Date オブジェクトJSON由来の文字列は通らない
z.null()null のみunion の部品として使う
z.undefined()undefined のみ同上
z.any()何でも検証を放棄する。型は any
z.unknown()何でも検証しないが型は unknown のまま
z.never()何も通らない「ここには値が来ないはず」の表明

よくあるハマりどころ

.min() の戻り値をifで判定してしまう

.min(3) が返すのはスキーマであって検証結果ではありません。結果がほしいときは parse か safeParse を呼びます。

.trim() を後ろに書いて空文字が通る

前述のとおりです。変換系は検証より先に書きます。

z.boolean() にフォームの値を渡して落ちる

HTMLのフォームやURLクエリから届く値は文字列です。"true" は z.boolean() を通りません。文字列から真偽値に変換したい場合は z.stringbool() を使います。こちらは "false" をちゃんと false に変換してくれます(z.coerce.boolean() は "false" を true にしてしまうので注意してください)。

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

  • 検証メソッドは新しいスキーマを返す。元のスキーマは変わらない
  • .trim() などの変換は検証より先に書く。順番で結果が変わる
  • .nonempty() は .min(1) と同じで、空白だけの入力は通る
  • 数値の 0 を許すかどうかで .positive() と .nonnegative() を使い分ける
  • メールやURLはZod 4ではトップレベルの z.email() / z.url()

次の章では、作ったスキーマを実際に使う parseとsafeParseの違い を扱います。例外で落とすか、結果を受け取って自分で分岐するか——実務ではほぼ後者を使うことになります。

参考リンク

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