【React】Zodで日付複合バリデーションを行う

ZodはTypescriptに適したバリデーションライブラリです。

よくあるユースケースとしては、ReactなどTypescriptベースのフレームワークで開発するアプリケーションでの入力フォームにおけるバリデーションがあるでしょう。

今回は、バリデーションの中で比較的複雑な、複数のフィールドにまたがるバリデーション(以下、複合バリデーションという)の方法をご紹介します。
例として、複数のまたがる日付の入力フィールドに関する複合バリデーションの方法を解説します。

複合バリデーションに使えるrefine関数

複合バリデーションをするには、Zodのrefine関数が便利です。

refine関数を用いると、複数フィールドの入力値を使ってカスタムの検証ロジックを作成することができます。

refine関数は以下の2つの引数を取ります。

  1. 検証関数
    バリデーションで正となる条件を指定します。この条件をフィールドの値が満たさない場合、エラーとなします。
  2. エラーハンドリングの方法を決定する複数のパラメータを指定できます。
    パラメータには3種類あります。
    1. message:エラーメッセージ
    2. path:エラーパス
    3. params:エラーメッセージに指定するパラメータをオブジェクト形式で指定できます。

例えば、refine関数を用いた複合バリデーションは以下のように実装できます。

const myString = z.string().refine((args) => args.base * args.height <= 100, {
  message: "面積は100以下にする必要があります。",
});

ここで、argsはZodのバリデーションで定義した他のフィールドの識別子です。

この例では、baseheightという2つのフィールドの値を使って検証を行なっています。

日付の比較をするバリデーション

では、「入力する日付が今日の日付よりも未来であることを検証する」バリデーションを考えます。

この時、「年」、「月」、「日」をそれぞれ別のフィールドで入力するものとします。(それぞれpickUpYear、pickUpMonth、pickUpDayとします)

実装コードは以下です。

z.object({  
 pickUpYear: z
        .string()
        .regex(/^(?!0).*$/, '年は必須項目です。')
        .regex(
          /^\d{4}/,
          '年は4文字で記入してください。'
        ),
      pickUpMonth: z
        .string()
        .regex(/^(?!0).*$/, '月は必須項目です。')
        .regex(
          /^\d{1,2}/,
          '月は2文字で記入してください。'
        ),
      pickUpDay: z
        .string()
        .regex(/^(?!0).*$/, '日は必須項目です。')
        .regex(
          /^\d{1,2}/,
          '日は2文字で記入してください。'
        ),
    })
    .refine(
      (args) => {
        const { pickUpYear, pickUpMonth, pickUpDay } = args;
        const inputDate = new Date(Number(pickUpYear), Number(pickUpMonth) - 1, Number(pickUpDay));
        const nowDate = new Date();
        return inputDate > nowDate;
      },
      { message: '日付は本日より未来である必要があります。' }
    );

年、月、日の各フィールドで必須バリデーションと文字数バリデーションを行なっています。

最後のrefine関数で3つのフィールドを組み合わせた複合バリデーションを実装しています。

まとめ

今回は、Zodを用いて、年月日を別々で入力させる場合の複合バリデーションの実装方法を紹介しました。

refine関数は日付以外にもバリデーションの条件をカスタムできる汎用性の高い機能ですのでぜひ使いこなしてみてください。

コメント

タイトルとURLをコピーしました