【無料配布】10日間で学べるTypeScript学習資料

TypeScript×Zodで型安全を極める!基本から実践的な活用術まで徹底解説

目次

TypeScriptの「静的な壁」とZodの必要性

「APIから返ってきたデータに型を付けたのに、実行してみたら undefined で落ちた……」

これは、TypeScriptの型が「コンパイル時」にしか存在しないためです。

一度JavaScriptにトランスパイルされ、ブラウザやNode.jsで実行される段階になると、あの堅牢な型チェックの盾は消えてなくなります。

外部からやってくるAPIレスポンスや、ユーザーが入力するフォームデータは、あなたの型定義を無視して「どんな汚いデータ」でも入り込めてしまうのです。

この「静的な型」と「動的な実データ」のギャップを埋めるのが、スキーマ駆動バリデーションライブラリ「Zod」です。

Zodは、実行時にデータを検証し、なおかつその検証ルールからTypeScriptの型を自動生成してくれるライブラリです。

Zodの基本:スキーマ定義とパース(解析)の仕組み

Zodの使い方は非常に直感的です。

まず「データの形(スキーマ)」を定義し、それに対して実際のデータを流し込みます。

プリミティブ型とオブジェクトの定義

Zodでは、z オブジェクトからメソッドを呼び出すことでスキーマを構築します。

import { z } from 'zod';

// スキーマの定義
const UserSchema = z.object({
  id: z.number(),
  name: z.string().min(1),
  email: z.string().email(),
  role: z.enum(['ADMIN', 'USER']).optional(),
});

単なる型定義以上の「ルール(1文字以上、メール形式である、特定の文字列のみなど)」を同時に宣言できるのがZodの強みです。

parse vs safeParse:エラーハンドリングの使い分け

定義したスキーマを使ってデータを検証する方法は主に2つあります。

スクロールできます
メソッド特徴適したケース
parse検証に失敗すると即座にエラー(例外)を投げる異常データが来たら処理を止めるべき場合
safeParse成功・失敗の結果をオブジェクトで返す(例外なし)失敗時に柔軟な処理(ログ出力や代替表示)をしたい場合
const result = UserSchema.safeParse(data);
if (!result.success) {
  console.error(result.error.format()); // エラー内容を可視化
  return;
}
const validUser = result.data; // ここでは型が確定している

型推論の魔法:z.infer で二重管理を撲滅する

Zodが「TypeScript-first」と言われる最大の理由が z.infer です。

通常、バリデーションライブラリを使うと「バリデーションルール」と「TypeScriptのInterface」を別々に書かなければならず、修正漏れの原因になります。

Zodなら、スキーマから型を自動抽出できます。

const UserSchema = z.object({ ... });

// スキーマからTypeScriptの型を生成
type User = z.infer<typeof UserSchema>;

// これだけで、User interface を手書きしたのと同じ効果が得られる

「スキーマを直したのに型を直し忘れた」という初歩的なミスがこの世から消滅します。

「スキーマこそが唯一の正解(Source of Truth)」になります。

バリデーションの深掘り:文字列チェックからカスタム検証まで

Zodのバリデーション機能は非常に強力で、日常的な要件のほとんどをカバーしています。

豊富な組み込みバリデーター

文字列一つとっても、実務で必要なチェックが網羅されています。

組み込みバリデーター
  • .email(): 正しいメールアドレス形式か
  • .url(): URL形式か
  • .uuid(): UUID形式か
  • .includes("foo"): 特定の文字列を含むか
  • .regex(/.../): 正規表現によるチェック

refineによる独自のビジネスロジック実装

「パスワードと確認用パスワードが一致しているか」といった、複数のフィールドにまたがるチェックには .refine() を使います。

const PasswordSchema = z.object({
  password: z.string(),
  confirm: z.string(),
}).refine((data) => data.password === data.confirm, {
  message: "パスワードが一致しません",
  path: ["confirm"], // エラーを出す場所を指定
});

実践編:Zodをどこで使うべきか?

Zodをどこで使うかは、主に以下の2点に絞られます。

Zodをどこで使うか
  • APIレスポンスの型保証(ランタイムの盾)
  • React Hook Form との連携による最強のフォーム管理

APIレスポンスの型保証(ランタイムの盾)

APIから取得したデータをそのまま as User と型アサーションするのは危険です。

Zodを挟むことで、バックエンドの仕様変更や不具合にフロントエンドがいち早く気づけるようになります。

async function fetchUser() {
  const response = await fetch('/api/user');
  const rawData = await response.json();
  
  // 実行時にデータを検証し、正しい場合のみ型を付ける
  return UserSchema.parse(rawData);
}

React Hook Form との連携による最強のフォーム管理

Reactでのフォームバリデーションにおいて、Zodは標準的な選択肢となりました。

@hookform/resolvers/zod を使うことで、複雑なバリデーションロジックをReactコンポーネントから切り離し、純粋なスキーマとして管理できます。

Zodを導入して「真の型安全」を手に入れる

TypeScriptは素晴らしい言語ですが、それは「コンパイル」という安全な温室の中での話です。

一歩外の世界(API、ユーザー入力、ローカルストレージ)に出れば、そこは無法地帯です。

Zodを導入することは、その無法地帯との境界線に「検問所」を設置することと同じです。

Zodによるメリット
  • 型とバリデーションの一元管理
  • 実行時のクラッシュ防止
  • 開発者体験(DX)の向上

Zodを利用することで、あなたのプロジェクトは「壊れにくい」堅牢性を獲得します。

まだ導入していない方は、ぜひ小さなAPIレスポンスの検証から始めてみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次