APIのレスポンスをそのままTypeScriptで扱うには、対応する型定義(InterfaceまたはType alias)が必要です。大きなJSONを手動でインターフェースに変換する作業は時間がかかるうえにミスも生まれます。
本記事では、JSONからTypeScriptの型を自動生成する方法・ツール・実際の変換例を解説します。REST API・GraphQL・LocalStorageのデータを型安全に扱いたい方に向けた実践ガイドです。
なぜJSON→TypeScript変換が必要か
TypeScriptを使う最大のメリットは型安全性です。しかし fetch や axios で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型 |
|---|---|
string | string |
number(整数・小数) | number |
true / false | boolean |
null | null(またはオプション ?) |
[ ... ](配列) | 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 alias:
interfaceとtype =の両方に対応 - readonly:すべてのプロパティに
readonlyを付与するオプション - null → optional:
null値を| 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で型を得るアプローチも有効。