Anúncios incomodam? Ir Sem anúncios Hoje

JSON to TypeScript Gerar interfaces automaticamente a partir de respostas de API

Atualizado em

Manually writing TypeScript interfaces for every API response is tedious and error-prone. Learn how to auto-generate accurate interfaces from real JSON data, then add runtime validation with Zod — because types disappear at runtime and `any` is not an answer.

JSON to TypeScript: Auto-Generate Interfaces From API Responses 1
ANUNCIADO Remover?

You’ve just fetched data from a third-party API. The response is a dense JSON blob — nested objects, arrays, nullable fields — and now you have to figure out how to type it in TypeScript. So you open a new file, start pecking out interface User { ... }, and 20 minutes later you’ve got something that probably matches the actual data. Probably.

There’s a better way. Tools that convert JSON directly to TypeScript interfaces take that 20-minute chore down to seconds. This article walks through what those generated types look like, how to handle the awkward cases (nulls, unions, deep nesting), and why you should pair generated types with Zod schemas to catch shape mismatches at runtime — not just at compile time.

Why TypeScript Interfaces for API Responses Matter

TypeScript’s value proposition is catching errors before your code runs. Without typed API responses, you’re flying blind: accessing properties that might not exist, treating optional values as required, or silently converting a string "null" into something unexpected downstream.

Consider this common scenario:

const user = await fetchUser(id);
console.log(user.address.city); // TypeError at runtime if address is null

If you’d typed the response properly — with address: Address | null — TypeScript would have flagged that access immediately. The compiler is your first line of defense, but only if you give it something to work with.

Manually writing interfaces for every API is tedious and error-prone. You misread the schema, miss an optional field, or copy-paste a stale version. Generating interfaces directly from real JSON data removes that human error from the equation.

What the JSON-to-TypeScript Conversion Produces

Take a straightforward API response:

{
  "id": 42,
  "username": "jsmith",
  "email": "j@example.com",
  "createdAt": "2024-01-15T10:30:00Z",
  "role": "admin",
  "profile": {
    "bio": "Developer",
    "avatar": null
  }
}

Paste that into the Gerador de Interfaces TypeScript a partir de JSON and you get:

interface Profile {
  bio: string;
  avatar: null;
}

interface RootObject {
  id: number;
  username: string;
  email: string;
  createdAt: string;
  role: string;
  profile: Profile;
}

A few things to note:

  • Nested objects become their own interfacesProfile is extracted automatically rather than inlined.
  • Dates are typed as string — JSON has no date type, so ISO strings stay as strings. You’ll need to parse them yourself.
  • avatar: null is typed as literal null — which is accurate but incomplete. More on that below.

Handling Tricky Cases

Nullable Fields

When a field is null in your sample JSON, the generator types it as null. But in practice, that field probably switches between a real value and null depending on the data. You’ll want to adjust those manually:

// Generated
avatar: null;

// What you actually want
avatar: string | null;

The same goes for optional fields that happen to be populated in your sample — add ? to any property that may be absent in some responses.

Matrizes

Arrays of objects are handled cleanly. Given:

{
  "posts": [
    { "id": 1, "title": "Hello", "published": true },
    { "id": 2, "title": "World", "published": false }
  ]
}

The generator produces:

interface Post {
  id: number;
  title: string;
  published: boolean;
}

interface RootObject {
  posts: Post[];
}

Tipos de União

If your JSON samples show a field holding different types across different records — say, a value that can be a number or a string — you should represent that as a union. Generated types won’t catch this from a single sample, so it’s worth checking against your API documentation:

value: string | number;

Deeply Nested Objects

Deep nesting is where manual typing really breaks down — and where generators earn their keep. A response with three or four levels of nesting gets decomposed into a clean hierarchy of named interfaces, each responsible for its own shape.

The Gap Between Compile-Time Types and Runtime Reality

Here’s the thing TypeScript newcomers often miss: types disappear at runtime. TypeScript compiles to JavaScript, and JavaScript has no concept of interfaces. If your API returns a shape that doesn’t match your declared type, TypeScript won’t know — and won’t tell you.

This makes the common pattern of casting API responses genuinely dangerous:

const data = await response.json() as User; // No validation, just trust

That cast tells TypeScript “trust me, this is a User” — but TypeScript has no way to verify it. If the API changes its shape or returns an error object instead, your code breaks at runtime in ways the compiler never warned you about.

The solution is runtime validation.

Zod for Runtime Validation

Zod is a TypeScript-first schema validation library. You define a schema once, use it to parse incoming data, and get back a fully-typed value — or a detailed error if the shape doesn’t match. No casting, no guessing.

Using the same JSON sample from before, the Gerador de Esquema Zod de JSON produces:

import { z } from "zod";

const ProfileSchema = z.object({
  bio: z.string(),
  avatar: z.null(),
});

const RootObjectSchema = z.object({
  id: z.number(),
  username: z.string(),
  email: z.string(),
  createdAt: z.string(),
  role: z.string(),
  profile: ProfileSchema,
});

type RootObject = z.infer<typeof RootObjectSchema>;

Notice the last line: z.infer derives the TypeScript type directly from the schema. You get both compile-time type safety and runtime validation from a single source of truth.

Using it at the fetch boundary looks like this:

const rawData = await response.json();
const user = RootObjectSchema.parse(rawData); // throws if shape is wrong

// Or use safeParse to avoid throwing:
const result = RootObjectSchema.safeParse(rawData);
if (!result.success) {
  console.error(result.error.issues);
} else {
  console.log(result.data.username); // fully typed
}

Adjust the generated schema to handle the nullable cases the generator can’t infer from a single sample:

avatar: z.string().nullable(), // was z.null()
bio: z.string().optional(),    // if the field might be absent

interface vs type: Which Should You Use?

Ambos interface e type aliases can represent object shapes in TypeScript, and for most API response typing they’re interchangeable. The practical differences:

  • Interfaces can be extended and merged — useful if you want to augment a base type across files. Declaration merging lets you add fields to an interface defined elsewhere.
  • Type aliases are more flexible — they can represent unions, intersections, tuples, and mapped types, which interfaces can’t.
  • Error messages tend to be clearer with interfaces — TypeScript expands type aliases in error output, which can make deeply nested errors harder to read.

For API response shapes, either works. Pick interface if you anticipate extending the type; use type if you need union or intersection semantics. Consistency within a codebase matters more than which one you choose.

The Practical Workflow

Here’s a workflow that takes minutes instead of an afternoon:

  1. Grab a real response. Use your browser’s DevTools network tab, Postman, or curl to capture an actual API response. The more complete the sample, the better the generated types.
  2. Generate the TypeScript interface. Paste the JSON into the Gerador de Interfaces TypeScript a partir de JSON. Copy the output into your project.
  3. Generate the Zod schema. Paste the same JSON into the Gerador de Esquema Zod de JSON. Copy that into your project as well.
  4. Review for nullable and optional fields. Scan the generated output for fields typed as literal null or fields that could be absent. Update those to string | null, .nullable(), ou .optional() as needed.
  5. Validate at the fetch boundary. Replace any as YourType casts with YourSchema.parse() ou safeParse(). Now the type is guaranteed to match, not just assumed.

That’s the full loop — from raw JSON to compile-time safety and runtime guarantees in a few minutes.

Quer eliminar anúncios? Fique sem anúncios hoje mesmo

Instale nossas extensões

Adicione ferramentas de IO ao seu navegador favorito para acesso instantâneo e pesquisa mais rápida

Ao Extensão do Chrome Ao Extensão de Borda Ao Extensão Firefox Ao Extensão Opera

O placar chegou!

Placar é uma forma divertida de acompanhar seus jogos, todos os dados são armazenados em seu navegador. Mais recursos serão lançados em breve!

ANUNCIADO Remover?
ANUNCIADO Remover?
ANUNCIADO Remover?

Notícias com destaques técnicos

Envolver-se

Ajude-nos a continuar fornecendo ferramentas gratuitas valiosas

Compre-me um café
ANUNCIADO Remover?