Create Generic TypeScript Wrapper Types from JSON Envelopes

Learn how to convert repeating JSON envelope patterns like pagination and API wrappers into reusable generic TypeScript types.

Complex Types

Detailed Explanation

Generic Wrappers from JSON Patterns

Many APIs wrap their data in a consistent envelope: a status field, pagination metadata, and the actual payload. Instead of duplicating this structure for every endpoint, you can create a generic wrapper type.

Example JSON (Users endpoint)

{
  "ok": true,
  "data": [{ "id": 1, "name": "Alice" }],
  "meta": { "page": 1, "total": 50, "perPage": 10 }
}

Example JSON (Products endpoint)

{
  "ok": true,
  "data": [{ "sku": "ABC-123", "price": 29.99 }],
  "meta": { "page": 1, "total": 200, "perPage": 10 }
}

Generated TypeScript

interface Meta {
  page: number;
  total: number;
  perPage: number;
}

interface ApiListResponse<T> {
  ok: boolean;
  data: T[];
  meta: Meta;
}

interface User {
  id: number;
  name: string;
}

interface Product {
  sku: string;
  price: number;
}

type UserListResponse = ApiListResponse<User>;
type ProductListResponse = ApiListResponse<Product>;

Detecting the Generic Pattern

The converter identifies a generic wrapper when:

  1. Multiple responses share an identical outer structure (same keys and same types for non-data fields).
  2. The data field varies between responses — different inner objects.
  3. The envelope keys (ok, meta, error) are consistent.

Single-Item Wrapper

For detail endpoints that return one item instead of a list:

interface ApiDetailResponse<T> {
  ok: boolean;
  data: T;
  meta?: never; // explicitly absent
}

type UserDetailResponse = ApiDetailResponse<User>;

Error Envelope

interface ApiErrorResponse {
  ok: false;
  error: {
    code: string;
    message: string;
  };
}

type ApiResponse<T> = ApiListResponse<T> | ApiErrorResponse;

Benefits

  • Single source of truth — Changing the envelope structure (e.g., adding a requestId) only requires updating the generic interface.
  • Type safetyUserListResponse guarantees that data contains User[], not Product[].
  • Less boilerplate — Endpoints reuse the same wrapper, reducing interface count by 50% or more in API-heavy codebases.

Use Case

Your API returns every response in the same envelope structure with ok, data, and meta fields, and you need a generic type that works for all endpoints instead of duplicating the wrapper 50 times.

Try It — JSON to TypeScript

Open full tool