JSONスキーマでZodの.transform()と.default()を使用する
JSONを検証済みの型付きデータに変換する際にZodの.transform()によるデータ変換と.default()によるフォールバック値の使い方を学びます。
Advanced
詳細な説明
Zodのtransformとdefault
Zodは単なるバリデーターではなく、パース中にデータを変換することもできます。これにより、乱雑なAPIレスポンスをクリーンな型付きデータに正規化するのに最適です。
.default() — フォールバック値
import { z } from "zod";
const configSchema = z.object({
theme: z.enum(["light", "dark"]).default("dark"),
pageSize: z.number().int().default(20),
showSidebar: z.boolean().default(true),
locale: z.string().default("en"),
});
type Config = z.infer<typeof configSchema>;
// 欠落フィールドにデフォルトが適用される
configSchema.parse({});
// { theme: "dark", pageSize: 20, showSidebar: true, locale: "en" }
configSchema.parse({ theme: "light" });
// { theme: "light", pageSize: 20, showSidebar: true, locale: "en" }
.transform() — データ変換
const userSchema = z.object({
name: z.string().transform((s) => s.trim()),
email: z.string().email().transform((s) => s.toLowerCase()),
createdAt: z.string().datetime().transform((s) => new Date(s)),
tags: z.string().transform((s) => s.split(",").map((t) => t.trim())),
});
userSchema.parse({
name: " Alice ",
email: "ALICE@Example.COM",
createdAt: "2024-03-15T10:30:00Z",
tags: "dev, design, pm",
});
// {
// name: "Alice",
// email: "alice@example.com",
// createdAt: Dateオブジェクト,
// tags: ["dev", "design", "pm"]
// }
入力型 vs 出力型
transformを使用すると、入力型と出力型が異なります:
const schema = z.string().transform((s) => parseInt(s, 10));
type Input = z.input<typeof schema>; // string
type Output = z.output<typeof schema>; // number(z.inferと同じ)
フォーム/API型にはz.inputを、内部型にはz.output(またはz.infer)を使用します。
.preprocess() — バリデーション前の変換
const numberFromString = z.preprocess(
(val) => (typeof val === "string" ? Number(val) : val),
z.number().min(0).max(100)
);
numberFromString.parse("42"); // 42
numberFromString.parse(42); // 42
numberFromString.parse("abc"); // ZodError(NaNはz.number()に失敗)
z.coerce — 組み込み型強制
// .preprocess()のよりシンプルな代替
z.coerce.number().parse("42"); // 42
z.coerce.boolean().parse("true"); // true
z.coerce.date().parse("2024-03-15T10:30:00Z"); // Dateオブジェクト
z.coerce.string().parse(42); // "42"
transformのチェーン
const slugSchema = z.string()
.min(1)
.transform((s) => s.toLowerCase())
.transform((s) => s.replace(/\s+/g, "-"))
.transform((s) => s.replace(/[^a-z0-9-]/g, ""));
slugSchema.parse("Hello World!"); // "hello-world"
transformにより、Zodは受動的なバリデーターから、バリデーションと正規化を1ステップで行うアクティブなデータ処理パイプラインに変わります。
ユースケース
フォーマットが一貫しない複数のサードパーティAPI(大文字小文字が混在するメール、数値の代わりの文字列、欠落するオプショナルフィールド)からデータを受け取り、すべてを一貫した内部フォーマットに正規化するZodスキーマが必要な場合に使用します。