Utilo

JSON to TypeScript Interfaces: API の応答をタイプするための実践的なガイド

JSON を正確な TypeScript インターフェースに変換し、オプションのフィールド、ゼロ、ユニオン、配列を処理し、タイプを実際の API と同期する方法を学びます

· 10 分で読めます

インタフェースと通信するすべての TypeScript プロジェクトが最終的に同じ疑問に直面しますタイプされていない JSON として届くデータの形をどのように記述しますか?anyとして応答を残すことは、TypeScriptの主な利点を捨てます。200行分のペイロードのインターフェースを手書きするのは退屈で誤りやすいものです。このガイドでは JSON からインターフェースを生成し、精製し、時間とともに正直に保つための信頼性の高いワークフローについて説明します。

JSON のタイプが重要な理由

await res.json()を呼び出すと TypeScriptが anyを返します。その瞬間から user.adress.city のようなタイプエラーは苦情なくコンパイルされ実行時に失敗するだけです生産中やデータが異なるユーザーのサブセットではしばしばです。バックエンドがフィールドの名前を変更するときに自動完了、安全なリファクタリング、コンパイル時のエラーが表示されます。

タイプはドキュメントとして機能します。interface Order { id: string; items: OrderItem[]; total: number; couponCode?: string }を読んでいる新しいチームメンバーは 5秒でサンプル応答をスクロールするより多くを学びます

ステップ1:実際の代表的なサンプルから開始

生成されたタイプは供給したサンプルと同じくらい良いのです。変換する前に、可能な限り多くのフィールドを実行する応答を収集します:

  • クーポン、配送アドレス、プレゼントメッセージを含む注文など、選択可能なフィールドを記入したレコードを選択します。
  • 少なくとも1つの要素を持つ配列を含みます。空の配列は生成元にその内容について何も伝えません。
  • フィールドが null になる場合、null でない場合の 1 つの例と、ない場合の 1 つの例を含みます。

ブラウザのネットワークタブまたはcurlから応答ボディをコピーし、[JSONフォーマット] ((/tools/json) に貼り付けて、有効であることを確認します。そして、モードを → TypeScript に切り替える。

ステップ2:最初の草案を作成

このインプットを考慮して:

{
  "id": "ord_9f2",
  "createdAt": "2026-09-30T12:01:44Z",
  "customer": { "id": 42, "email": "ana@example.com" },
  "items": [
    { "sku": "TSHIRT-M", "qty": 2, "price": 19.5 },
    { "sku": "MUG", "qty": 1, "price": 12 }
  ],
  "coupon": null
}

発電機は以下のようなものを生成します

export interface Customer {
  id: number;
  email: string;
}

export interface Item {
  sku: string;
  qty: number;
  price: number;
}

export interface Root {
  id: string;
  createdAt: string;
  customer: Customer;
  items: Item[];
  coupon: null;
}

これは良い出発点ですが一つのサンプルが含んでいたものだけを表しています。次のステップは API 契約と一致するタイプに変換します

ステップ3: 改名と改定

ジェネレーターはキーでネストされたインターフェースを名付けます。Rootは Orderになり Itemは OrderItemになります。この質問を用いて各欄をレビューしてください。

** 欠落する可能性がありますか?** APIがいくつかの応答で鍵を省略した場合、それを?でオプションにマークします。選択式 (coupon?: Coupon) と無効式 (coupon: Coupon | null) は異なる.最初の意味では鍵が欠けている場合もあるが、第二の意味では鍵は常に存在しているが、nullを保持している場合もある。Many APIs do both、which you express as coupon?: Coupon | null。

**nullが唯一の値だったか?**文字どおり nullと入力されたフィールドは、ほぼ常にサンプルが代表的でないことを意味します。| null を加えた本物型に置き換えます

**文字列は本当に文字列ですか?**日付はISO 8601文字列として表示されます。輸送型では string として保存する。Date に変換することはマッピング層に属します。type ISODateString = stringという偽名で意図を記録できます

数字は数列ですか? statusが常に数値の1つである場合、文字の組合: status: "pending" | "paid" | "shipped"を使用します。switchの詳細をチェックします

2^53以上のデータベースIDはJavaScriptで精度を失います。文字列としてIDを送信して string と入力してください

精製された結果:

export type ISODateString = string;

export interface Order {
  id: string;
  createdAt: ISODateString;
  status: "pending" | "paid" | "shipped" | "cancelled";
  customer: Customer;
  items: OrderItem[];
  coupon?: Coupon | null;
}

ステップ4: 混合コンテンツを持つ配列を処理する

いくつかのAPIは、異なるイベントタイプを持つアクティビティフィードのような異質な配列を返します。素朴な発電機は任意の各フィールドを単一のインターフェースで生成し情報を失います。差別のない組合の方がずっといい

type FeedEvent =
  | { type: "comment"; id: string; text: string }
  | { type: "like"; id: string; userId: string }
  | { type: "follow"; id: string; followerId: string };

if (event.type === "comment") の内部に自動的にタイプを絞り込みます。混合配列を変換する際に、ツールが推論された項目のタイプを結合します。その出力をヒントとして使用して、差別化された組合を手動で設計します。

ステップ 5:実行時に検証する

TypeScript タイプはコンパイル時に消える。バックエンドが変わってもコードが新しい形になってタイプが黙って嘘をつくのです。信頼境界を越えるデータについては、Zod、Valibot、ArkTypeなどのスキーマライブラリで実行時の検証を追加し、スキーマから静的タイプを導きます。

import { z } from "zod";

const OrderSchema = z.object({
  id: z.string(),
  createdAt: z.string().datetime(),
  items: z.array(z.object({ sku: z.string(), qty: z.number().int(), price: z.number() })),
  coupon: z.object({ code: z.string() }).nullable().optional(),
});

export type Order = z.infer<typeof OrderSchema>;
const order = OrderSchema.parse(await res.json());

インターフェースを生成しフィールドごとにスケーマに変換できますインターフェースの生成は

ステップ 6 真実の源があるという場合、真理の源を優先する

OpenAPI (Swagger) または GraphQL スキーマを公開している場合は、サンプルではなく、その中でタイプを生成します。openapi-typescriptやGraphQLコードジェネレーターのようなツールでは、オプションや数値を含む正確なタイプが生成され、CIで実行できますので、スキーマが変更されるたびにタイプが更新されます。スキーマ、迅速なプロトタイプ、ウェブフック、レガシーサービスのない第三者APIでサンプルベースの生成が望ましい

インターフェイスかタイプか?

オブジェクト形状では、interfaceとtypeはほとんど互換性があります。インターフェースは拡張・合併され、エラーメッセージは名前で参照され、大型オブジェクトでは便利です。ユニオン、タプル、マッピングされたタイプにはタイプアリアスが必要です。共通のコンベンションはオブジェクト形状のインターフェースであり、他のすべてのもののタイプアリアスです。

共通の罠

  • 安全な感じがしますがどこにでもゼロチェックを強要し契約違反を隠します。フィールドをオプションにするのは、APIがそれらを省略した場合に限ります。
  • *単一のサンプルを信頼する。 * 選択性を決定する前に、ドキュメントや複数の回答を確認する。
  • トランスポートとドメインタイプを混合する. JSONを映し出す原始OrderDTOを保存し、実際のDateオブジェクトと計算されたフィールドを含むドメインOrderにマッピングする。
  • **ページナレーションのラッパーについて忘れてしまいます.**多くのAPIは{ data: [...], nextCursor: "..." }でリストをラッパーします。interface Page<T> { data: T[]; nextCursor: string | null } と表します。

簡単なチェックリスト

  1. 豊かな実体サンプルを捕獲して検証します
  2. [JSON から TypeScript 変換](/tools/json とインターフェースを生成する。
  3. ドメイン言語にタイプを改名する。
  4. オプションと無効フィールド、文字結合とIDタイプを修正します。
  5. 組合を差別する組合に置き換える
  6. 外部データへの実行時の検証を追加する。
  7. OpenAPI または GraphQL スキーマが存在する場合、スキーマベースの生成に切り替える。

この手順を実行するには端末ごとに数分かかりますが自動で完了するより安全なリファクタやバグがユーザーに届く前に検出されるので倍に返済されます

このページは英語から自動翻訳されています。誤りを見つけた場合はお知らせください。

関連ガイド