APIのレスポンスをそのままTypeScriptで扱うには、対応する型定義(InterfaceまたはType alias)が必要です。大きなJSONを手動でインターフェースに変換する作業は時間がかかるうえにミスも生まれます。

本記事では、JSONからTypeScriptの型を自動生成する方法・ツール・実際の変換例を解説します。REST API・GraphQL・LocalStorageのデータを型安全に扱いたい方に向けた実践ガイドです。

なぜJSON→TypeScript変換が必要か

TypeScriptを使う最大のメリットは型安全性です。しかし fetchaxios でAPIを呼んだとき、レスポンスの型は自動では付きません。any を使うとTypeScriptの恩恵がほぼゼロになります。

// ❌ any では型チェックが効かない
const data: any = await fetch('/api/user').then(r => r.json());
console.log(data.nmae); // タイポしてもエラーにならない

// ✅ 型定義があれば補完とエラー検出が動く
interface User { id: number; name: string; email: string; }
const data = await fetch('/api/user').then(r => r.json()) as User;
console.log(data.nmae); // コンパイルエラー! 'nmae' は存在しない

JSON→TypeScript変換ツールは、このインターフェース定義を自動生成することで、手作業の手間とミスを大幅に削減します。

変換の基本ルール

JSONの各値は以下のTypeScript型にマッピングされます:

JSONの値TypeScript型
stringstring
number(整数・小数)number
true / falseboolean
nullnull(またはオプション ?
[ ... ](配列)Type[]
{ ... }(オブジェクト)ネストした interface

変換例:シンプルなUser

// 入力JSON
{
  "id": 1,
  "name": "山田 太郎",
  "email": "taro@example.com",
  "isActive": true,
  "score": 98.5
}

// 生成されるTypeScript
interface User {
  id: number;
  name: string;
  email: string;
  isActive: boolean;
  score: number;
}

変換例:ネストしたオブジェクト

// 入力JSON
{
  "id": 1,
  "name": "山田 太郎",
  "address": {
    "postalCode": "150-0001",
    "city": "渋谷区",
    "street": "神南1丁目"
  },
  "tags": ["premium", "verified"]
}

// 生成されるTypeScript
interface Address {
  postalCode: string;
  city: string;
  street: string;
}

interface User {
  id: number;
  name: string;
  address: Address;
  tags: string[];
}

nullを含む場合

// 入力JSON
{
  "id": 1,
  "name": "山田 太郎",
  "deletedAt": null,
  "bio": null
}

// null → オプショナルとして変換する場合
interface User {
  id: number;
  name: string;
  deletedAt?: string;
  bio?: string;
}

// null型を明示する場合
interface User {
  id: number;
  name: string;
  deletedAt: string | null;
  bio: string | null;
}

JSON to TypeScript ジェネレーターの使い方

JSON to TypeScript Interface Generator を使えば、JSONを貼り付けるだけでTypeScriptのインターフェースを自動生成できます。

  • Interface / Type aliasinterfacetype = の両方に対応
  • readonly:すべてのプロパティに readonly を付与するオプション
  • null → optionalnull 値を | null ではなく ?(オプショナル)に変換するオプション
  • ネストしたインターフェース:ネストしたオブジェクトを独立した名前付きインターフェースとして展開
  • サンプルJSON:User・Product・Order の3種類のサンプルで動作をすぐ確認できる

実務で使えるパターン集

APIレスポンス全体の型定義

// よくあるAPIのラッパー形式
{
  "success": true,
  "data": { ... },
  "meta": { "total": 100, "page": 1, "perPage": 20 }
}

// 生成後に手直しして再利用しやすくする
interface Meta {
  total: number;
  page: number;
  perPage: number;
}

interface ApiResponse {
  success: boolean;
  data: T;
  meta: Meta;
}

// 使い方
const res = await fetch('/api/users').then(r => r.json()) as ApiResponse;

Zodと組み合わせてバリデーション

import { z } from 'zod';

// JSON to TypeScriptで型のアイデアを得てからZodスキーマを書く
const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  isActive: z.boolean(),
});

type User = z.infer; // 型もスキーマから自動生成

// 実行時バリデーション付きのfetch
const data = UserSchema.parse(await fetch('/api/user').then(r => r.json()));

LocalStorageのデータ管理

// LocalStorageに保存するデータ構造をJSON化して型生成
interface AppSettings {
  theme: string;
  language: string;
  notifications: boolean;
  lastSyncAt: string;
}

function loadSettings(): AppSettings | null {
  const raw = localStorage.getItem('settings');
  if (!raw) return null;
  return JSON.parse(raw) as AppSettings;
}

生成後の手直しポイント

ツールが自動生成した型はベースとして優秀ですが、実用性を高めるためにいくつか手直しすると効果的です。

  • 配列要素の型を確認: 空配列 []never[]unknown[] になることがある。実際の型に直す
  • 日付文字列を特定: "2024-01-15T09:00:00Z"string と推定されるが、string(ISO日付)とコメントするか Date へのパース処理を追加する
  • ユニオン型の活用: status: string より status: 'active' | 'inactive' | 'banned' の方が型安全
  • インターフェース名の変更: ツールは自動で名前を付けるが、ドメインに合った意味のある名前に変更する
  • Genericsの活用: ページネーション・APIラッパーなど繰り返しパターンはジェネリクスで抽象化する

よくある質問(FAQ)

interface と type alias はどちらを使うべきですか?
どちらも同様の用途に使えますが、公式ガイドラインはオブジェクト型に interface、ユニオン型や複雑な型演算に type を推奨しています。拡張(extends)が必要な場合は interface、ユニオン型や交差型には type が便利です。チーム内で統一することが重要です。
JSONの配列が空のとき型はどうなりますか?
空配列 [] から型を推論する場合、ツールによって never[]unknown[] になります。実際のAPIから値が入ったレスポンスを使って型生成するか、生成後に適切な型(例:string[])に手動で修正してください。
大きなJSONはどうすればよいですか?
ページネーションされたAPIなら1ページ分のサンプルレスポンスで十分です。大きなJSONをそのまま変換すると不要なネストやプロパティが増えるため、代表的な構造を持つ最小限のサンプルを使うことをお勧めします。
Zodスキーマを先に書いてTypeScriptの型を得る方法もありますか?
はい。ZodはTypeScriptファーストなバリデーションライブラリで、z.infer<typeof Schema> でスキーマから型を自動生成できます。ランタイムバリデーションが必要な場合はZodがおすすめです。型定義のみ欲しい場合はインターフェースで十分です。
readonly を付けるべき場面は?
APIレスポンスのように変更すべきでないデータには readonly を付けると、意図しない書き換えをコンパイル時に防げます。フォームの入力値など変更が必要なデータには付けないのが一般的です。

まとめ

  • APIレスポンスに型を付けることで、TypeScriptの型安全性とIDEの補完が最大限に発揮される。
  • JSON to TypeScript ジェネレーターでインターフェースのベースを自動生成し、手直しで精度を上げるのが効率的なワークフロー。
  • null値は | null またはオプショナル ? に変換できる——APIの仕様に合わせて選択する。
  • 実務では自動生成した型に日付型のコメント・ユニオン型・Genericsの適用などを加えて仕上げる。
  • ランタイムバリデーションが必要な場合はZodと組み合わせて z.infer で型を得るアプローチも有効。