🙆‍♀️

【Zod】superRefine() と .refine()の違い

に公開

superRefine()と.refine()

superRefine().refine()は Zodでのカスタムバリデーションを行う際の2つの方法で、それぞれの使いどころが異なります。

機能名 用途 特徴
.refine() 単一のフィールド(値)に対する追加検証 値を見て true/false を返す
superRefine() オブジェクトや複数フィールドを跨ぐ検証に使う 複雑なロジックや相関チェックが可能

.refine()

目的

Zodの組み込みバリデーションでは対応できない独自ルールを追加したい時に使う。

特徴

特徴 説明
カスタムバリデーション 独自ルールを追加できる
真偽値を返す true なら OK、false ならエラー
任意のエラーメッセージ message で指定可能
パスも指定できる path を使えばエラーの表示場所を細かく制御できる
どんなスキーマにも使える string, number, object, array などすべてに使える

基本構文

z.string().refine(
  value => 条件式,
  {
    message: "エラーメッセージ",
    path: ["任意の", "パス"] // 省略可能
  }
)

使用例

18歳以上かどうか

const ageSchema = z.number().refine(
  (val) => val >= 18,
  { message: "18歳未満は登録できません" }
);

// 判定
ageSchema.parse(20); // OK
ageSchema.parse(15); // ❌ エラー: "18歳未満は登録できません"

パスワードの複雑さチェック

const passwordSchema = z.string().refine(
  val => /[A-Z]/.test(val) && /[0-9]/.test(val),
  { message: 'パスワードには大文字と数字を含めてください' }
);

.superRefine()

目的

通常の .refine() は単一のフィールド(文字列や数値など)に対する簡単な条件チェックに使いますが、superRefine() はそれだけでは実現できない以下のような目的に使われます。

  • 複数のフィールドの相関チェック
  • 条件付きバリデーション(特定の値があるときのみ他の値を必須にするなど)
  • バリデーションエラーを任意のフィールドに追加する(フォームUIのエラーメッセージ表示に便利)

特徴

特徴 説明
複数フィールドの検証 オブジェクトの全データを対象にチェックできる
柔軟なエラー制御 エラーの追加先(path)を自由に指定できる
複雑なロジックに対応 if/else などの条件分岐、ループ、他の処理を含んだ検証が可能
非同期バリデーション対応 async 関数として定義すれば非同期処理も可能(APIでの重複チェックなど)

基本構文

z.object({
  // フィールド定義
}).superRefine((data, ctx) => {
  if (バリデーション失敗条件) {
    ctx.addIssue({
      path: ['エラーを出したいフィールド名'],
      code: z.ZodIssueCode.custom,
      message: 'エラーメッセージ',
    });
  }
});

使用例

パスワードと確認用パスワードの一致チェック

const schema = z.object({
  password: z.string(),
  confirmPassword: z.string(),
}).superRefine((data, ctx) => {
  if (data.password !== data.confirmPassword) {
    ctx.addIssue({
      path: ['confirmPassword'],
      code: z.ZodIssueCode.custom,
      message: 'パスワードが一致しません',
    });
  }
});

開始日が終了日より前であることを検証

const dateSchema = z.object({
  startDate: z.date(),
  endDate: z.date(),
}).superRefine((data, ctx) => {
  if (data.startDate >= data.endDate) {
    ctx.addIssue({
      path: ['startDate'],
      code: z.ZodIssueCode.custom,
      message: '開始日は終了日より前にしてください',
    });
  }
});

違いまとめ

比較項目 .refine() superRefine()
バリデーション対象 スキーマ単体の値 スキーマ全体(主にオブジェクト)
フィールド相関チェック ❌ 不可 ✅ 複数フィールドをまたいだチェック可能
エラー追加の柔軟性 ❌ 限定的(そのフィールドのみ) ✅ 任意のフィールド・複数エラーを自由に追加可能
使用場面 数値の範囲、文字列の形式など簡単な検証 日付の前後、confirmPasswordとの一致など
  • data:そのオブジェクトのバリデーション対象となる入力データ
  • ctx:エラーを追加するための「文脈オブジェクト」
  • ctx.addIssue():任意のフィールドにバリデーションエラーを登録する

使い分け

ユースケース 推奨メソッド
メール形式、文字長チェックなど単体 .refine()
パスワードと確認用パスワードの一致 superRefine()
「開始日 < 終了日」などの相関バリデーション superRefine()
値があれば形式チェック、なければOK .refine()(または .optional().refine(...)

Discussion