[Zod]or, union, discriminatedUnion

orやunionは、複数の型のどれか1つを許容するバリデーションを定義するときに使う。

import { z } from "zod";

const stringOrNumber = z.string().or(z.number());

こんなかんじ。 orの代わりにunionを使うと3つ以上の型を許容できる。

const stringOrNumber = z.union([z.string(), z.number()]);

discriminatedUnionは複数の型のうち、特定のキーを基にして型を区別できるユニオン型を作成できる。 z.unionとの違いは特定のキーを使うこと。オブジェクトがどの型なのかをキーで判別できる。 パフォーマンス的にもz.unionはすべての型を試行するが、discriminatedUnionはキーを使った型を一発で特定できるので処理速度が速い。

import { z } from "zod";

const Dog = z.object({
  type: z.literal("dog"),
  breed: z.string(),
});

const Cat = z.object({
  type: z.literal("cat"),
  lives: z.number(),
});

const Animal = z.discriminatedUnion("type", [Dog, Cat]);

Animal.parse({ type: "dog", breed: "Labrador" }); // OK
Animal.parse({ type: "cat", lives: 9 });         // OK
Animal.parse({ type: "dog", lives: 9 });         // エラー
Animal.parse({ type: "fish", fins: 2 });         // エラー

例えばAPIのリクエストやレスポンスでオブジェクトの種類が異なるときに便利

const CreditCardPayment = z.object({
  type: z.literal("credit_card"),
  cardNumber: z.string(),
  cvv: z.string(),
});

const PayPalPayment = z.object({
  type: z.literal("paypal"),
  email: z.string(),
});

const Payment = z.discriminatedUnion("type", [CreditCardPayment, PayPalPayment]);

Payment.parse({
  type: "credit_card",
  cardNumber: "1234-5678-9876-5432",
  cvv: "123",
}); // OK

Payment.parse({
  type: "paypal",
  email: "example@paypal.com",
}); // OK

Payment.parse({
  type: "credit_card",
  email: "example@paypal.com",
}); // エラー