JSON to TypeScript Gerar interfaces automaticamente a partir de respostas de API
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.
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 interfaces —
Profileis 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: nullis typed as literalnull— 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:
- 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.
- Generate the TypeScript interface. Paste the JSON into the Gerador de Interfaces TypeScript a partir de JSON. Copy the output into your project.
- Generate the Zod schema. Paste the same JSON into the Gerador de Esquema Zod de JSON. Copy that into your project as well.
- Review for nullable and optional fields. Scan the generated output for fields typed as literal
nullor fields that could be absent. Update those tostring | null,.nullable(), ou.optional()as needed. - Validate at the fetch boundary. Replace any
as YourTypecasts withYourSchema.parse()ousafeParse(). 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.
Instale nossas extensões
Adicione ferramentas de IO ao seu navegador favorito para acesso instantâneo e pesquisa mais rápida
恵 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!
Ferramentas essenciais
Ver tudo Novas chegadas
Ver tudoAtualizar: Nosso ferramenta mais recente foi adicionado em 7 de junho de 2026
