Back to Blog
·7 min read

Generating TypeScript Types from JSON API Responses

Why typing your API responses matters, how to generate accurate interfaces from real JSON samples, and how to avoid the most common typing mistakes.

If you're writing TypeScript and consuming a JSON API, you have two options: type your responses properly, or sprinkle "any" everywhere and lose most of what makes TypeScript useful. Writing interfaces by hand for every endpoint is tedious — but generating them from a real response sample takes seconds and produces far more accurate types than guessing from documentation (which is often out of date).

Why "any" defeats the purpose

Typing a response as "any" turns off type checking for that value and everything derived from it. You lose autocomplete, you lose compile-time errors when a field is renamed or removed, and worst of all, the type silently propagates — once "any" enters your codebase, it tends to spread.

// With "any" — no safety, no autocomplete, typos go unnoticed
const res: any = await fetchUser()
console.log(res.naem)  // typo — compiles fine, fails at runtime

// With a proper interface — caught immediately
interface User { name: string; age: number }
const res: User = await fetchUser()
console.log(res.naem)  // Property 'naem' does not exist on type 'User'

Generating an interface from a real sample

The fastest, most accurate way to type a response is to grab a real example — from your browser's network tab, a Postman response, or your API's docs — and generate the interface directly from it. This captures the actual shape of the data your code will receive, not an idealized version of it.

{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com",
  "roles": ["admin", "editor"],
  "address": { "city": "Austin", "zip": "73301" },
  "lastLogin": null
}
interface Address {
  city: string
  zip: string
}

interface User {
  id: number
  name: string
  email: string
  roles: string[]
  address: Address
  lastLogin: string | null
}

JSON to TypeScript Generator

Paste a JSON sample and get nested, accurately-typed TypeScript interfaces in seconds — including arrays, optional fields, and unions.

Handling optional and nullable fields correctly

Real-world APIs are messy — fields that are sometimes present, sometimes null, and sometimes entirely absent are the norm, not the exception. Getting this distinction right in your types prevents both false compile errors and missed runtime checks.

  • ·A field that can be null but is always present → string | null
  • ·A field that may be entirely absent from the response → optional with ?: phone?: string
  • ·A field that is sometimes null AND sometimes absent → combine both: phone?: string | null

The best way to get this right is to look at multiple real samples — a single response might not show you every variation a field can take.

Going further: runtime validation with JSON Schema

TypeScript interfaces are a compile-time-only construct — they vanish at runtime and provide zero protection against an API actually returning something different than expected (a backend bug, a breaking API change, a malformed response). For genuinely critical data paths, pair your TypeScript types with runtime validation using a JSON Schema or a library like Zod.

JSON Type Generator

Generate a JSON Schema from a sample — a great companion to TypeScript interfaces for runtime validation of API responses.

JSON Schema Validator

Validate that incoming API responses actually match your expected shape before trusting them in your application.

Keeping types in sync as APIs evolve

  • ·Regenerate interfaces whenever you notice an API response shape has changed — don't hand-patch generated types
  • ·Prefer generating types from a live or recent sample over relying solely on documentation, which often lags behind the actual API
  • ·For large APIs, consider generating types from an OpenAPI/Swagger spec if one is maintained and kept current
  • ·Add runtime validation at the boundary (where data enters your app) so a shape mismatch fails loudly instead of causing a confusing bug three layers downstream

Tip: Treat generated interfaces as a starting point, not gospel — review them, rename auto-generated nested type names to something meaningful, and add JSDoc comments where the field names alone aren't self-explanatory.

Ad