一部記事にはアフィリエイト広告が含まれています。

初心者エンジニア、TypeScriptの「Zod」をゼロから学ぶ

kaede
kaede

この記事は、私のプログラミング学習の過程を記録しています。内容が必ずしも最新かつ正確であることを保証するものではないので、参考にする際は公式ドキュメントなどもご確認ください。

【基礎】Zodの使い方を丁寧に解説

Zodとは、データの型を定義してバリデーションチェックできるライブラリである。
入力フォームのバリデーションチェック(文字制限など)などに使われる。

Zodの使い方をゼロから解説

1. zodライブラリのインストール
JavaScriptで使える「外部ライブラリ」のためインストールが必要。

npm install zod

2. zodライブラリからzオブジェクトをインポート
zオブジェクトの中に、データのスキーマ(型の定義)を作る関数など、全機能が用意されている。

import { z } from "zod";

3. スキーマ(データの型ルール)を定義
z.object():スキーマをつくる関数
z.string():「文字列であるべき」というバリデーションルールを定義する関数
z.number():「数値であるべき」というバリデーションルールを定義する関数

const schema = z.object({
  name: z.string(), // nameプロパティの値は「文字列」
  age: z.number(), // ageプロパティの値は「数値」
});

4. データ型をチェック
スキーマ.safeParse():スキーマに基づいてデータが正しいかどうかをチェックする関数
引数にチェックしたいデータをオブジェクトとして設定

const result1 = schema.safeParse({ name: "かえで", age: 25 }); 
const result2 = schema.safeParse({ name: "もみじ", age: 15 }); 

5. 結果を確認・活用
例)結果をコンソール出力

console.log(result1);

// 成功 
{
  "success": true,
  "data": { "name": "かえで", "age": 25 }
}

console.log(result2);

// 失敗
{
  "success": false,
  "error": {
    // バリデーションエラーのリスト(配列)
    "issues": [
      {
        "code": "too_small", // エラーの種類
        "message": "age must be greater than or equal to 18", // エラーメッセージ
        "path": ["age"] // どのフィールドでエラーが発生したか
      }
    ]
  }
}

例)エラー結果をformat()で出力
error.format():フィールドごとにエラーを整理して返す関数
          特定のフィールドにエラーがあるかどうかをチェックできる

console.log(result2.error.format());

// 出力
{
  "_errors": [], // フォーム全体のエラーが入る(例:入力が全くない場合など)
  "age": {
    "_errors": ["age must be greater than or equal to 18"] // ageフィールドで発生したエラー
  }
}

// 年齢フィールドに対するエラーのみを出力
const errors = result2.error.format(); 
if(!result2.success) {
  console.log(errors.age._errors);
 // 出力: ["age must be greater than or equal to 18"]
}

バリデーションチェックの応用

const schema = z.object({
  name: z.string({
    
  }), // nameプロパティの値は「文字列」
  age: z.number({
    
  }), // ageプロパティの値は「数値」
});

よく使うバリデーションルール

ルール説明
z.string()文字列
z.number()数値
z.boolean()真偽値
z.array(z.string())文字列の配列
z.string().min(5)最低5文字以上
z.number().max(100)最大100以下
z.string().email()メールアドレス
z.string().regex(/^\d+$/)正規表現チェック

【実践】React Hook Form ‪‪✕‬ zod でバリデーションチェック

React Hookの復習はこちら
初心者エンジニア、「React」をゼロから学ぶ

zodResolverreact-hook-formzod を連携させるためのブリッジのような役割
        useFormでのフォーム送信時にzodでバリデーションチェックができる

useFormresolver プロパティに zodResolver を渡すことで、フォーム送信時に自動的に zod スキーマに基づくバリデーションチェックが行われる。

const { 
  register, handleSubmit, formState: { errors } 
} = useForm({
  resolver: zodResolver(schema),
});

例)ユーザー情報のスキーマを定義してバリデーションチェック

import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

// スキーマ定義
const schema = z.object({
  email: z.string().email(),   // メールアドレスは有効なメール形式で
  password: z.string().min(6),  // パスワードは6文字以上
});

export default function App() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(schema),  // ここでzodResolverを使用
  });

  const onSubmit = (data) => {
    console.log("成功:", data);  
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <input {...register("email")} placeholder="メールアドレス" />
        {errors.email && <p>{errors.email.message}</p>} // react-hook-formの書き方
      </div>
      <div>
        <input {...register("password")} type="password" placeholder="パスワード" />
        {errors.password && <p>{errors.password.message}</p>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
}

react-hook-formではなくzodの型チェックを使う理由

・入力データの型チェックができる(詳細は次の章)
・react-hook-formのバリデーションより 簡潔に書ける
・複雑なバリデーションチェックが簡単にできる
・コードの再利用がしやすい(スキーマを他で使える)
リアルタイムでバリデーションチェックを行いエラーを表示さえせる

【応用】ZodでTypeScriptの型を設定しよう

ZodFormattedErrorでエラーのデータ型を設定

ZodFormattedError:エラーのデータの形を TypeScript に教えるための「型」
            result.error.format()の返り値の型を指定できる

// result.error.format()の返り値の例
// この複雑なオブジェクトの形をZodFormattedErrorで簡単に指定することが可能

{
  "_errors": [], // フォーム全体のエラーが入る(例:入力が全くない場合など)
  "age": {
    "_errors": ["age must be greater than or equal to 18"] // ageフィールドで発生したエラー
  }
}

✔️ ZodFormattedErrorの型の設定方法

// 引数にはバリデーション対象のデータの「型」を指定する

ZodFormattedError<{ name: string; age: number }>

✔️ ZodFormattedError(エラーのデータ型)はなぜ必要?

・エラーのデータの形を TypeScript に教えるため
・型を教えないと、TypeScript が「このデータの形ってどうなってるの?」と迷うことがある
・型を指定することで、エディタの補助が効きやすくなり、バグを防ぎやすくなる

✔️ 実際の活用例

import { z, ZodFormattedError } from "zod";

const schema = z.object({
  name: z.string(),
  age: z.number().min(18),
});

// バリデーション対象のデータ
const data = { name: "かえで", age: 16 };

const result = schema.safeParse(data);

if (!result.success) {
  const errors: ZodFormattedError<{ name: string; age: number }> = result.error.format();
  console.log(errors.age?._errors[0])
}
// 出力結果

"age must be greater than or equal to 18"

z.inferでZodのスキーマの方をつくる

z.infer:ZodのスキーマからTypeScript の型を自動で作るための機能

// Zodのスキーマ

const userSchema = z.object({
  name: z.string(),
  age: z.number(),
});

// TypeScript用の型を作成

type User = z.infer<typeof userSchema>

✔️ 自分で型を定義する場合

// TypeScriptの型定義
// type User = z.infer<typeof user Schema> と全く同じ

type User = {
  name: string;
  age: number;
};

・せっかくZodで型の情報を定義したのに、TypeScript の型を手書きするのは面倒
・書き間違いやスキーマの変更による、スキーマと型の不整合が発生する可能性

 → z.infer を使うことで、Zod のスキーマからTypeScriptの型を自動でつくれる

✔️ Zodスキーマ の型の使い方

// 使用例

import { z } from "zod";

const userSchema = z.object({
  name: z.string(),
  age: z.number(),
});

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

// 「age: "25"」の場合、コンパイル時にエディタ上でエラー発生(ほぼリアルタイム)
const params = { name: "かえで", age: 25 } as  User;

// 「age: "25"」の場合、実行時にターミナルやコンソールでエラー発生(npm run dev)
userSchema.safeParse(params);

as User:paramsの型を明確に指定
params の型を User だと TypeScript に伝えている
     { name: “かえで”, age: 25 }の型が、User型でない場合、エディタ上でエラーが発生
safeParse:paramsがuserSchemaスキーマに基づいて正しいかどうかをチェック
       { name: “かえで”, age: 25 }がuserSchemaと異なる場合、エラーが発生

二重でチェックしてくれることにより、安全性が高まる

kaede
kaede

参考文献について
この記事は、公式ドキュメントや他のウェブサイトを参考にしながら、プログラミング学習の内容をまとめています。特に、ChatGPT(OpenAI)の情報を多く取り入れており、自分の言葉でわかりやすく要約しています。

コメント