T-CREATOR

Zod 合成パターン早見表:`object/array/tuple/record/map/set/intersection` 実例集

Zod 合成パターン早見表:`object/array/tuple/record/map/set/intersection` 実例集

TypeScript でバリデーションを行う際、Zod は非常に強力なツールです。しかし、複雑なデータ構造を扱う場合、どの合成パターンを使うべきか迷うことはありませんか。

本記事では、Zod の主要な合成パターンである objectarraytuplerecordmapsetintersection を実例とともに解説します。それぞれの特徴や使い分けを理解することで、より正確で保守性の高いバリデーションコードを書けるようになるでしょう。

Zod 合成パターン早見表

各パターンの特徴と使い分けを一覧で確認できます。

#パターン用途構造の特徴主な使用例
1object固定キーのオブジェクトキーと型が明確に定義されているAPI レスポンス、設定オブジェクト
2array同じ型の要素リスト要素数は可変、全て同じ型ユーザーリスト、タグ配列
3tuple固定長・型違いリスト要素数と各位置の型が固定座標 [x, y]、API 戻り値 [data, error]
4record動的キーのオブジェクトキー名が事前に不明、値の型は統一多言語辞書、設定マップ
5mapキーと値のペア集合ES6 Map 構造、キー・値に任意の型キャッシュ、ID マッピング
6set重複なし要素集合ES6 Set 構造、要素の順序は保証なしタグセット、ユニーク ID 集合
7intersection複数スキーマの結合AND 条件で型を合成共通プロパティ+固有プロパティ

背景

TypeScript における型安全性の重要性

TypeScript は静的型付けによって、コンパイル時にエラーを検出できます。しかし、実行時に外部から受け取るデータ(API、ユーザー入力、環境変数など)は、TypeScript の型システムだけでは保証できません。

ここでランタイムバリデーションが必要になります。Zod は、スキーマ定義とバリデーションを統合し、TypeScript の型推論も自動で行える画期的なライブラリです。

Zod が解決する課題

従来のバリデーションライブラリでは、以下のような課題がありました。

  • バリデーションロジックと TypeScript の型定義を別々に管理する必要がある
  • 型定義とバリデーションの不整合が発生しやすい
  • 複雑なデータ構造のバリデーションが煩雑になる

Zod は、スキーマから自動的に TypeScript の型を生成することで、これらの課題を解決します。

データ構造と合成パターンの関係

以下の図は、様々なデータ構造と Zod の合成パターンの対応関係を示しています。

mermaidflowchart TD
  data["受信データ"] --> check{"データ構造は?"}
  check -->|"固定キー<br/>オブジェクト"| obj["z.object()"]
  check -->|"同じ型の<br/>リスト"| arr["z.array()"]
  check -->|"固定長<br/>型違いリスト"| tup["z.tuple()"]
  check -->|"動的キー<br/>オブジェクト"| rec["z.record()"]
  check -->|"Map構造"| map["z.map()"]
  check -->|"Set構造"| set["z.set()"]
  check -->|"複数スキーマ<br/>の合成"| int["z.intersection()"]

  obj --> result["型安全な<br/>バリデーション"]
  arr --> result
  tup --> result
  rec --> result
  map --> result
  set --> result
  int --> result

データ構造に応じて適切なパターンを選ぶことで、効率的かつ正確なバリデーションを実現できます。

課題

合成パターンの選択に迷う場面

Zod を使い始めた開発者が直面する主な課題は、「どのパターンを使えばいいのか」という判断です。特に以下のような場面で迷いが生じます。

動的なキーを持つオブジェクトの扱い

API から返される設定データで、キー名が実行時に決まる場合があります。このとき objectrecord のどちらを使うべきでしょうか。

配列とタプルの使い分け

一見似ている arraytuple ですが、用途は大きく異なります。要素数が可変か固定か、全て同じ型か位置ごとに型が異なるかが判断基準です。

Map や Set のバリデーション

ES6 で導入された MapSet は、オブジェクトや配列とは異なる特性を持ちます。これらをどうバリデーションするかは、意外と見落とされがちです。

複数のスキーマを組み合わせたい場合

基本的なプロパティを持つスキーマに、状況に応じて追加のプロパティを結合したいケースがあります。このとき intersection を使うと、コードの再利用性が高まります。

以下の図は、よくある課題パターンを整理したものです。

mermaidflowchart LR
  dev["開発者"] --> q1{"キーは<br/>事前に判明?"}
  q1 -->|YES| use_obj["object を使用"]
  q1 -->|NO| use_rec["record を使用"]

  dev --> q2{"要素数は<br/>固定?"}
  q2 -->|NO| use_arr["array を使用"]
  q2 -->|YES| use_tup["tuple を使用"]

  dev --> q3{"スキーマを<br/>結合?"}
  q3 -->|YES| use_int["intersection を使用"]
  q3 -->|NO| single["単一スキーマ"]

この判断フローを理解することで、適切なパターン選択ができるようになります。

解決策

各パターンの明確な使い分け

Zod の合成パターンは、データ構造の特性に応じて使い分けることが重要です。

固定構造には object

キーと型が事前に決まっている場合は object を使います。これにより、プロパティの存在チェックと型チェックを同時に行えます。

同じ型の可変長リストには array

ユーザーリストや商品リストなど、要素数が変動し、全て同じ型の場合は array が最適です。

位置ごとに型が異なる固定長リストには tuple

座標 [x, y] や、API の戻り値 [data, error] のように、各位置の意味と型が決まっている場合は tuple を使いましょう。

動的なキーには record

多言語対応の辞書データなど、キー名が実行時に決まる場合は record を使います。キーの型(通常は string)と値の型を指定できます。

ES6 Map には map

キーと値のペアを管理する Map 構造をバリデーションする場合は、z.map() を使います。

ES6 Set には set

重複を許さない要素の集合である Set 構造には、z.set() を使います。

複数スキーマの合成には intersection

共通のベーススキーマに、状況に応じて追加プロパティを結合する場合は intersection を使います。これにより、スキーマの再利用性が向上します。

パターン選択のフローチャート

以下の図は、データ構造からパターンを選択する流れを示しています。

mermaidstateDiagram-v2
  [*] --> データ受信
  データ受信 --> キー構造確認

  state キー構造確認 {
    [*] --> 固定キー判定
    固定キー判定 --> object: キーが事前に確定
    固定キー判定 --> record: キーが動的
  }

  データ受信 --> リスト構造確認

  state リスト構造確認 {
    [*] --> 要素型判定
    要素型判定 --> array: 全て同じ型
    要素型判定 --> tuple: 位置ごとに型が異なる
  }

  データ受信 --> コレクション構造確認

  state コレクション構造確認 {
    [*] --> コレクション型判定
    コレクション型判定 --> map: Map構造
    コレクション型判定 --> set: Set構造
  }

  データ受信 --> スキーマ合成確認

  state スキーマ合成確認 {
    [*] --> 合成判定
    合成判定 --> intersection: 複数スキーマを結合
  }

  object --> [*]
  record --> [*]
  array --> [*]
  tuple --> [*]
  map --> [*]
  set --> [*]
  intersection --> [*]

このフローに従うことで、迷わずパターンを選択できます。

具体例

パターン 1:object - 固定キーのオブジェクト

object は最も基本的なパターンで、キーと型が明確に定義されているオブジェクトをバリデーションします。

基本的な使い方

API から返されるユーザー情報をバリデーションする例です。

typescriptimport { z } from 'zod';

// ユーザースキーマの定義
const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(0).max(150),
});

上記のコードでは、idnameemailage という 4 つの固定キーを持つオブジェクトを定義しています。それぞれのプロパティに対して、型とバリデーションルールを指定できます。

型の推論と使用

Zod スキーマから自動的に TypeScript の型を生成できます。

typescript// スキーマから型を推論
type User = z.infer<typeof UserSchema>;

// 実際のバリデーション
const userData = {
  id: 1,
  name: '山田太郎',
  email: 'yamada@example.com',
  age: 30,
};

const result = UserSchema.safeParse(userData);

safeParse メソッドを使うことで、エラーが発生してもプログラムが停止せず、結果オブジェクトで成功・失敗を判定できます。

オプショナルプロパティの扱い

必須ではないプロパティは optional() で定義します。

typescriptconst UserWithOptionalSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  age: z.number().optional(), // 年齢は任意
  bio: z.string().optional(), // 自己紹介文も任意
});

これにより、agebio は存在しなくてもバリデーションが通るようになります。

パターン 2:array - 同じ型の要素リスト

array は、同じ型の要素を可変長で持つリストをバリデーションします。

基本的な配列のバリデーション

文字列の配列(タグリスト)をバリデーションする例です。

typescript// タグ配列のスキーマ
const TagsSchema = z.array(z.string());

// 使用例
const tags = ['TypeScript', 'Zod', 'バリデーション'];
const result = TagsSchema.safeParse(tags);

z.array() の引数に、要素の型スキーマを渡すことで、配列全体をバリデーションできます。

配列の長さ制限

配列の要素数に制限を設けることも可能です。

typescript// 最低1つ、最大5つのタグを持つ配列
const LimitedTagsSchema = z
  .array(z.string())
  .min(1, { message: 'タグは最低1つ必要です' })
  .max(5, { message: 'タグは最大5つまでです' });

.min().max() メソッドを使うことで、配列の長さを検証し、カスタムエラーメッセージも設定できます。

オブジェクトの配列

実務では、オブジェクトの配列をバリデーションする場面が多くあります。

typescript// 商品情報のスキーマ
const ProductSchema = z.object({
  id: z.number(),
  name: z.string(),
  price: z.number().positive(),
});

// 商品リストのスキーマ
const ProductListSchema = z.array(ProductSchema);

// 使用例
const products = [
  { id: 1, name: 'ノートPC', price: 150000 },
  { id: 2, name: 'マウス', price: 3000 },
];

const result = ProductListSchema.safeParse(products);

このように、objectarray を組み合わせることで、複雑なデータ構造も扱えます。

パターン 3:tuple - 固定長・型違いリスト

tuple は、要素数が固定で、各位置の型が異なるリストをバリデーションします。

座標データのバリデーション

2 次元座標 [x, y] をバリデーションする例です。

typescript// 座標のスキーマ(数値2つのタプル)
const CoordinateSchema = z.tuple([
  z.number(), // x座標
  z.number(), // y座標
]);

// 使用例
const point = [100, 200];
const result = CoordinateSchema.safeParse(point);

z.tuple() には、配列で各位置のスキーマを渡します。この例では、1 番目と 2 番目の要素がともに number 型であることを指定しています。

異なる型を持つタプル

API の戻り値で、データとエラーを返すパターンをバリデーションします。

typescript// [データ, エラー] の形式
const ApiResponseSchema = z.tuple([
  z.object({ message: z.string() }).nullable(), // データ(nullの可能性あり)
  z.string().nullable(), // エラーメッセージ(nullの可能性あり)
]);

// 成功時の例
const successResponse = [{ message: '成功しました' }, null];
const result1 =
  ApiResponseSchema.safeParse(successResponse);

// エラー時の例
const errorResponse = [null, 'エラーが発生しました'];
const result2 = ApiResponseSchema.safeParse(errorResponse);

タプルを使うことで、位置によって異なる型を持つデータ構造を正確にバリデーションできます。

残余要素を持つタプル

固定の要素に加えて、残りの要素も受け入れる場合は .rest() を使います。

typescript// 最初の2要素は固定、残りは文字列
const MixedTupleSchema = z
  .tuple([
    z.number(), // 1番目:ID
    z.string(), // 2番目:名前
  ])
  .rest(z.string()); // 3番目以降:タグ(可変)

// 使用例
const data = [1, '商品A', 'タグ1', 'タグ2', 'タグ3'];
const result = MixedTupleSchema.safeParse(data);

これにより、固定部分と可変部分を両方持つタプルを定義できます。

パターン 4:record - 動的キーのオブジェクト

record は、キー名が事前に分からないオブジェクトをバリデーションします。

多言語辞書のバリデーション

言語コードをキーとした翻訳データをバリデーションする例です。

typescript// キーは文字列、値も文字列のレコード
const TranslationSchema = z.record(
  z.string(), // キーの型
  z.string() // 値の型
);

// 使用例
const translations = {
  ja: 'こんにちは',
  en: 'Hello',
  fr: 'Bonjour',
  de: 'Guten Tag',
};

const result = TranslationSchema.safeParse(translations);

z.record() の第 1 引数がキーの型、第 2 引数が値の型を表します。

キーに制約を設けるパターン

キーを特定の列挙型に制限したい場合は、enum と組み合わせます。

typescript// 許可された言語コードのみを受け入れる
const LanguageCodeSchema = z.enum(['ja', 'en', 'fr', 'de']);

const StrictTranslationSchema = z.record(
  LanguageCodeSchema, // キーは特定の言語コードのみ
  z.string()
);

// 正常なデータ
const validData = {
  ja: 'こんにちは',
  en: 'Hello',
};

// エラーになるデータ('es'は許可されていない)
const invalidData = {
  ja: 'こんにちは',
  es: 'Hola', // エラー
};

これにより、キーの値も厳密にバリデーションできます。

値に複雑なスキーマを持つレコード

値がオブジェクトの場合も、同様に定義できます。

typescript// ユーザー設定のスキーマ
const UserSettingSchema = z.object({
  enabled: z.boolean(),
  value: z.number(),
});

// 設定項目名をキーとしたレコード
const SettingsSchema = z.record(
  z.string(),
  UserSettingSchema
);

// 使用例
const settings = {
  notifications: { enabled: true, value: 10 },
  autoSave: { enabled: false, value: 5 },
};

const result = SettingsSchema.safeParse(settings);

キーは動的でも、値の構造は厳密に定義できる点が record の強みです。

パターン 5:map - キーと値のペア集合

map は、ES6 の Map オブジェクトをバリデーションします。

基本的な Map のバリデーション

ユーザー ID をキー、ユーザー情報を値とした Map をバリデーションする例です。

typescript// ユーザー情報のスキーマ
const UserInfoSchema = z.object({
  name: z.string(),
  email: z.string().email(),
});

// Map のスキーマ(キー:number、値:UserInfo)
const UserMapSchema = z.map(
  z.number(), // キーの型
  UserInfoSchema // 値の型
);

z.map() の第 1 引数がキーの型スキーマ、第 2 引数が値の型スキーマです。

Map の使用例

実際に Map オブジェクトを作成してバリデーションします。

typescript// Mapの作成
const userMap = new Map<number, any>();
userMap.set(1, {
  name: '田中',
  email: 'tanaka@example.com',
});
userMap.set(2, { name: '佐藤', email: 'sato@example.com' });

// バリデーション
const result = UserMapSchema.safeParse(userMap);

if (result.success) {
  // 型安全なMapとして使用可能
  result.data.forEach((user, id) => {
    console.log(`ID: ${id}, 名前: ${user.name}`);
  });
}

Map を使うことで、オブジェクトよりも柔軟なキーの型を扱えます。

キーに複雑な型を使う Map

キーが文字列や数値以外の場合にも対応できます。

typescript// キーがオブジェクトのMap
const ComplexKeySchema = z.object({
  category: z.string(),
  subcategory: z.string(),
});

const ComplexMapSchema = z.map(
  ComplexKeySchema,
  z.number() // 値は数値
);

// 使用例
const complexMap = new Map();
complexMap.set(
  { category: '食品', subcategory: '野菜' },
  100
);
complexMap.set(
  { category: '食品', subcategory: '果物' },
  200
);

const result = ComplexMapSchema.safeParse(complexMap);

この柔軟性が、Mapz.map() の大きな利点です。

パターン 6:set - 重複なし要素集合

set は、ES6 の Set オブジェクトをバリデーションします。

基本的な Set のバリデーション

重複を許さないタグのセットをバリデーションする例です。

typescript// 文字列のSetスキーマ
const TagSetSchema = z.set(z.string());

// Setの作成
const tagSet = new Set([
  'TypeScript',
  'Zod',
  'バリデーション',
]);

// バリデーション
const result = TagSetSchema.safeParse(tagSet);

z.set() の引数に、要素の型スキーマを渡します。

Set の要素数制限

Set のサイズに制限を設けることもできます。

typescript// 最低1つ、最大10個の要素を持つSet
const LimitedSetSchema = z
  .set(z.string())
  .min(1, { message: '最低1つの要素が必要です' })
  .max(10, { message: '最大10個までです' });

// 使用例
const validSet = new Set(['タグ1', 'タグ2']);
const result1 = LimitedSetSchema.safeParse(validSet);

// エラーになる例(空のSet)
const emptySet = new Set();
const result2 = LimitedSetSchema.safeParse(emptySet);

.min().max() を使って、Set のサイズを検証できます。

複雑な型を要素に持つ Set

オブジェクトを要素とする Set もバリデーション可能です。

typescript// ユーザーIDのスキーマ
const UserIdSchema = z.object({
  type: z.enum(['admin', 'user', 'guest']),
  id: z.number(),
});

// ユーザーIDのSetスキーマ
const UserIdSetSchema = z.set(UserIdSchema);

// 使用例
const userIdSet = new Set([
  { type: 'admin', id: 1 },
  { type: 'user', id: 2 },
  { type: 'guest', id: 3 },
]);

const result = UserIdSetSchema.safeParse(userIdSet);

Set を使うことで、重複を自動的に排除しつつ、バリデーションも行えます。

パターン 7:intersection - 複数スキーマの結合

intersection は、複数のスキーマを AND 条件で結合します。

基本的な intersection の使い方

共通のプロパティを持つベーススキーマと、追加プロパティを持つスキーマを結合する例です。

typescript// ベーススキーマ(共通プロパティ)
const BaseEntitySchema = z.object({
  id: z.number(),
  createdAt: z.date(),
  updatedAt: z.date(),
});

// 商品固有のプロパティ
const ProductPropsSchema = z.object({
  name: z.string(),
  price: z.number().positive(),
  stock: z.number().min(0),
});

// 結合
const ProductSchema = z.intersection(
  BaseEntitySchema,
  ProductPropsSchema
);

これにより、idcreatedAtupdatedAtnamepricestock の全てを持つスキーマが作成されます。

型の推論結果

intersection で結合されたスキーマから推論される型を確認します。

typescript// 型の推論
type Product = z.infer<typeof ProductSchema>;

// 結果は以下と同等
// {
//   id: number;
//   createdAt: Date;
//   updatedAt: Date;
//   name: string;
//   price: number;
//   stock: number;
// }

// 使用例
const product: Product = {
  id: 1,
  createdAt: new Date(),
  updatedAt: new Date(),
  name: 'ノートPC',
  price: 150000,
  stock: 10,
};

const result = ProductSchema.safeParse(product);

全てのプロパティが必須となるため、どちらかのスキーマに存在するプロパティが欠けているとエラーになります。

複数のスキーマを結合

3 つ以上のスキーマを結合することも可能です。

typescript// タイムスタンプスキーマ
const TimestampSchema = z.object({
  createdAt: z.date(),
  updatedAt: z.date(),
});

// 作成者情報スキーマ
const AuthorSchema = z.object({
  authorId: z.number(),
  authorName: z.string(),
});

// コンテンツスキーマ
const ContentSchema = z.object({
  title: z.string(),
  body: z.string(),
});

// 全てを結合
const ArticleSchema = z.intersection(
  TimestampSchema,
  z.intersection(AuthorSchema, ContentSchema)
);

// または、連鎖的に結合
const ArticleSchema2 =
  TimestampSchema.and(AuthorSchema).and(ContentSchema);

.and() メソッドを使うと、より読みやすく結合できます。

intersection の実用例:権限管理

ユーザータイプに応じて、異なるプロパティを持つスキーマを作成します。

typescript// 基本ユーザー情報
const BaseUserSchema = z.object({
  id: z.number(),
  username: z.string(),
  email: z.string().email(),
});

// 管理者権限
const AdminPropsSchema = z.object({
  role: z.literal('admin'),
  permissions: z.array(z.string()),
});

// 一般ユーザー権限
const UserPropsSchema = z.object({
  role: z.literal('user'),
  subscription: z.enum(['free', 'premium']),
});

// 管理者スキーマ
const AdminSchema = z.intersection(
  BaseUserSchema,
  AdminPropsSchema
);

// 一般ユーザースキーマ
const UserSchema = z.intersection(
  BaseUserSchema,
  UserPropsSchema
);

このパターンにより、共通部分を再利用しつつ、タイプごとの固有プロパティも定義できます。

パターンの組み合わせ例

実際のアプリケーションでは、複数のパターンを組み合わせて使用します。

API レスポンスの複雑なスキーマ

記事一覧 API のレスポンスをバリデーションする例です。

typescript// 作成者情報
const AuthorSchema = z.object({
  id: z.number(),
  name: z.string(),
});

// 記事情報
const ArticleSchema = z.object({
  id: z.number(),
  title: z.string(),
  body: z.string(),
  tags: z.array(z.string()), // array パターン
  author: AuthorSchema, // object パターン
  metadata: z.record(
    // record パターン
    z.string(),
    z.union([z.string(), z.number()])
  ),
});

// ページネーション情報
const PaginationSchema = z.object({
  page: z.number(),
  perPage: z.number(),
  total: z.number(),
});

// APIレスポンス全体
const ApiResponseSchema = z.object({
  articles: z.array(ArticleSchema), // array パターン
  pagination: PaginationSchema,
});

この例では、objectarrayrecord を組み合わせて、複雑な API レスポンスを型安全にバリデーションしています。

フォームデータのバリデーション

ユーザー登録フォームのデータをバリデーションする例です。

typescript// 基本情報
const BasicInfoSchema = z.object({
  username: z.string().min(3).max(20),
  email: z.string().email(),
  password: z.string().min(8),
});

// 追加情報(オプショナル)
const AdditionalInfoSchema = z.object({
  age: z.number().min(18).max(150).optional(),
  bio: z.string().max(500).optional(),
  interests: z.array(z.string()).optional(), // array パターン
});

// フォーム全体のスキーマ
const RegistrationFormSchema = z.intersection(
  BasicInfoSchema,
  AdditionalInfoSchema
);

// 使用例
const formData = {
  username: 'yamada_taro',
  email: 'yamada@example.com',
  password: 'securePass123',
  age: 30,
  interests: ['読書', 'プログラミング'],
};

const result = RegistrationFormSchema.safeParse(formData);

intersection を使うことで、必須項目とオプション項目を分けて管理できます。

エラーハンドリングのベストプラクティス

バリデーションエラーを適切に処理することは、ユーザー体験の向上につながります。

エラーメッセージのカスタマイズ

各バリデーションルールに、わかりやすいエラーメッセージを設定します。

typescriptconst UserSchema = z.object({
  username: z
    .string()
    .min(3, {
      message: 'ユーザー名は3文字以上で入力してください',
    })
    .max(20, {
      message: 'ユーザー名は20文字以内で入力してください',
    }),
  email: z
    .string()
    .email({
      message: '有効なメールアドレスを入力してください',
    }),
  age: z
    .number()
    .min(18, { message: '18歳以上である必要があります' }),
});

カスタムメッセージを設定することで、ユーザーに具体的な修正方法を伝えられます。

エラーの詳細な取得

safeParse の結果から、どのフィールドでエラーが発生したかを確認できます。

typescriptconst result = UserSchema.safeParse({
  username: 'ab', // 短すぎる
  email: 'invalid-email', // 不正な形式
  age: 15, // 18歳未満
});

if (!result.success) {
  // エラー情報の取得
  console.log(result.error.issues);

  // フィールドごとのエラーを整形
  const fieldErrors = result.error.flatten().fieldErrors;
  console.log(fieldErrors);
  // {
  //   username: ['ユーザー名は3文字以上で入力してください'],
  //   email: ['有効なメールアドレスを入力してください'],
  //   age: ['18歳以上である必要があります']
  // }
}

.flatten() メソッドを使うと、フィールドごとにエラーメッセージをグループ化できます。

まとめ

Zod の合成パターンを理解することで、あらゆるデータ構造を型安全にバリデーションできるようになります。

本記事では、7 つの主要パターンを解説しました。

固定構造には object を使い、プロパティを明確に定義します。API レスポンスや設定オブジェクトなど、構造が事前に決まっている場合に最適です。

可変長の同一型リストには array を使います。ユーザーリストや商品リストなど、要素数が変動するデータに適しています。

固定長で型が異なるリストには tuple を使います。座標や API の戻り値など、各位置の意味が明確な場合に有効です。

動的なキーを持つオブジェクトには record を使います。多言語辞書や設定マップなど、キー名が実行時に決まるデータに最適です。

ES6 の Map 構造には map を使い、キーと値のペアを柔軟にバリデーションします。

ES6 の Set 構造には set を使い、重複のない要素集合を扱います。

複数のスキーマを結合するには intersection を使い、コードの再利用性を高めます。

これらのパターンを適切に使い分けることで、保守性が高く、バグの少ないコードを書けるでしょう。Zod は、TypeScript の型システムと完全に統合されているため、バリデーションと型安全性を同時に実現できる強力なツールです。

実際のプロジェクトでは、複数のパターンを組み合わせて使用することが多くなります。本記事で紹介した実例を参考に、自分のプロジェクトに最適なスキーマ設計を行ってください。

関連リンク