T-CREATOR

Zodを活用した実践的なユースケースをいくつか紹介

Zodを活用した実践的なユースケースをいくつか紹介

Zodは基本的なバリデーションにとどまらず、複雑な型構造の検証や型共有、ユニオン型の条件付き分岐などにも対応可能です。

今回はより実践的なユースケースを紹介しながら、Zodの柔軟性と強力さを掘り下げていきます。


条件に応じた動的スキーマ分岐(Discriminated Union)

Zodでは条件付きのバリデーション分岐が可能です。これは、入力の内容に応じて構造が異なるフォームやAPIリクエストに非常に便利です。

tsconst AnimalSchema = z.discriminatedUnion("type", [
  z.object({
    type: z.literal("dog"),
    barkVolume: z.number(),
  }),
  z.object({
    type: z.literal("cat"),
    lives: z.number(),
  }),
]);

type Animal = z.infer<typeof AnimalSchema>;
tsAnimalSchema.parse({ type: "dog", barkVolume: 10 }); // OK
AnimalSchema.parse({ type: "cat", lives: 9 });       // OK
AnimalSchema.parse({ type: "cat", barkVolume: 10 }); // ❌ ValidationError

「type」の値によってスキーマが切り替わるため、構造が分岐するデータの検証にぴったりです。


バックエンドとフロントエンドでの型共有

API設計で特に効果を発揮するのがスキーマと型の共通管理です。
Zodのスキーマ定義を共通パッケージにまとめ、両者で型とバリデーションを再利用できます。

例:共通パッケージ構成

bash/packages/shared-schema
  └ user.ts ← zodスキーマと型定義
/frontend
  └ 使用:フォームの入力チェック、型定義
/backend
  └ 使用:リクエストバリデーション、レスポンス構築

共通スキーマ(shared-schema/user.ts)

tsimport { z } from "zod";

export const userSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1),
  email: z.string().email(),
});

export type User = z.infer<typeof userSchema>;

このようにしておけば、型崩れや重複定義を防ぎ、保守性と安全性が大幅に向上します。


入れ子構造のバリデーションと型推論

Zodではネスト構造もシンプルに記述できます。
以下はブログ記事とタグの入れ子構造の例です。

tsconst tagSchema = z.object({
  id: z.string(),
  label: z.string(),
});

const articleSchema = z.object({
  title: z.string(),
  content: z.string().min(20),
  tags: z.array(tagSchema),
});

このような構造でも、z.infer<typeof articleSchema>を使えば深い階層の型推論が正確に行われるため、IDE補完の精度も非常に高まります。


.transformを使った入力変換

Zodでは、バリデーションと同時に値の変換処理も可能です。

tsconst priceSchema = z
  .string()
  .transform((val) => parseFloat(val));

const result = priceSchema.parse("123.45"); // result: number型の123.45

APIやフォーム入力で「文字列で渡ってくるけど数値として使いたい」といった場合にも対応可能です。


.refine.superRefineによるカスタムルール

refineは1つの値に対する制約、superRefineは複数フィールドにまたがるバリデーションが行えます。

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

このように、フィールド同士の整合性検証も柔軟に設計できます。


まとめ

Zodを活用した高度なユースケースでは、以下のような強力な機能を使いこなせます。

機能活用シーン
Discriminated Uniontypeなどの条件に応じた構造の切り替え
型の共通化APIでの型・バリデーションの一元管理
.transform入力値の自動変換(例:文字列→数値)
.superRefine複数項目の相関チェック(例:パスワード)

これらを適切に活用することで、開発体験の向上、バグの削減、そして将来的な変更への強さが手に入ります。

Zodは型安全を保ちつつ、複雑な仕様にも耐えうる実用的なツールです。ぜひプロジェクトのコアに取り入れてみてください。

記事Article

もっと見る