Techouse Developers Blog

テックハウス開発者ブログ|マルチプロダクト型スタートアップ|エンジニアによる技術情報を発信|SaaS、求人プラットフォーム、DX推進

localStorage の値を Zod で安全にパースする

ogp

はじめに

この記事は、Techouse Advent Calendar 2024 8日目です。
昨日は 澤井(aaaa777) さんによる 配属1ヶ月のインターンが勝手にRBS::Inlineを導入して怒られた でした。 8日目は、高橋 (@Kaffff) が担当します。今回は、localStorage に保存された値を検証する場面で、Zod の parse 関数を利用した方法を紹介します。

筆者はこれまで localStorage から取得した値のパースに、JSON.parse を使っていましたが、Zod の parse 関数と組み合わせることで、より型安全に値をパースできました。

従来の方法 (JSON.parse のみを使う方法)

まずは、JSON.parse のみを使った従来の方法を確認してみましょう。

まず、保存したいデータの型を定義します。ここでは、User 型を定義しています。

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

localStorage には文字列しか保存できないため、オブジェクトを保存する際には JSON.stringify を使って JSON 文字列に変換する必要があります。

const user: User = { name: "山田 太郎", age: 20 };

localStorage.setItem("user", JSON.stringify(user));

保存した値を取得する際は、localStorage.getItem を使ってデータを取得します。localStorage.getItem で取得した値は文字列なので、JSON.parse を使ってオブジェクトに戻します。

ただし、この操作には注意が必要です。万が一、localStorage に格納されている値が JSON 形式ではなかった場合、エラーが発生します。

以下のコードでは、もしパースに失敗した場合には catch 節でエラーをキャッチし、localStorage の値を削除しています。

const item = localStorage.getItem("user") ?? "{}";

try {
  const user = JSON.parse(item) as User;
} catch {
  // パースに失敗した場合は localStorage の値を削除する
  localStorage.removeItem("user");
}

問題点

この方法で問題になるのは、型安全が保証されないことです。JSON.parse の結果として返される値は、型が保証されていません。そのため、型の不一致や予期しない構造のデータを扱う場合にバグが発生する可能性があります。

Zod を組み合わせた方法

そこで登場するのが、Zod です。 Zod とは TypeScript 向けのスキーマ宣言およびバリデーションを行うためのライブラリです。

zod.dev

Zod を使う場合、まずスキーマを定義します。ここでは z.object()z.string() 等を用いて、ユーザーのスキーマを定義しています。

次の type User = z.infer<typeof userSchema> では、Zod のスキーマから TypeScript の User 型を抽出しています。

import { z } from "zod";

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

type User = z.infer<typeof userSchema>;

localStorage にデータを保存する部分は、従来の方法と変わりません。

const user: User = { name: "山田 太郎", age: 20 };

localStorage.setItem("user", JSON.stringify(user));

localStorage から値を取得し、Zod でバリデーションを行うコードは以下のようになります。

ここでは、userSchema.parse を使って、JSON.parse した値がスキーマに従っているかを検証しています。

もし検証に失敗した場合は、ZodError を投げます。

const item = localStorage.getItem("user") ?? "{}";

try {
  const parsedItem = JSON.parse(item);
  const user = userSchema.parse(parsedItem);
} catch {
  // パースに失敗した場合は loclaStorage の値を削除する
  localStorage.removeItem("user");
}

JSON.parse との比較

JSON.parse は単に JSON 文字列 を JavaScript オブジェクトに変換するためのもので、データ型が適切かどうかは検証しません。 データが JSON 形式でない場合はエラーを投げますが、それ以外のバリデーションは行いません。

一方で、Zod の parse は、与えられたデータがスキーマに従っているかどうかを検証します。

特徴 JSON.parse Zod の parse
目的 JSON 文字列を JavaScript オブジェクトに変換 データがスキーマに適合するか検証する
バリデーション JSON 構文が正しいかどうかだけ確認 型や制約のバリデーションを実施
エラー処理 JSON が無効な場合、SyntaxErrorが投げられる バリデーションエラーがある場合、ZodErrorが投げられる
戻り値 有効な JSON 文字列から変換されたオブジェクト(any型) スキーマに一致するデータ (型はスキーマから生成される)

まとめ

普段、フォームのバリデーションに用いることが多い Zod ですが、フォームだけでなく localStorage や URL などアプリケーション外部からの入力全般に用いることができる非常に有用なライブラリです。

Zod を使用することで、型安全なコードを書き、バグのリスクを減らすことができます。ぜひ、皆さんも試してみてください。

最後まで読んでいただき、ありがとうございました。参考になれば幸いです!

明日のTechouse Advent Calendar 2024は titti-008 さんによる 資料作成ゼロ!ChatGPTで効率化する勉強会運営の秘密 です。

Techouse では、社会課題の解決に一緒に取り組むエンジニアを募集しております。 ご応募お待ちしております。

jp.techouse.com