Como tipar a resposta de uma API em TypeScript

O fetch entrega os dados com o tipo any, o que desliga o TypeScript exatamente onde os bugs nascem. Veja como dar um tipo de verdade aos dados da API, com rapidez e segurança.

O TypeScript verifica o seu código, mas não consegue verificar dados que chegam em tempo de execução. No momento em que você chama await response.json(), recebe um valor do tipo any, e a partir daí o compilador deixa você ler user.adress.city, com erro de digitação e tudo, sem reclamar. Tipar as respostas da API fecha essa brecha. Este guia mostra como escrever os tipos rapidamente a partir de um exemplo, como modelar as partes complicadas e quando só um tipo não basta.

Passo 1: tire os tipos de um exemplo real

Escrever interfaces à mão para uma resposta grande é lento e sujeito a erros. Em vez disso, copie uma resposta real, por exemplo da aba Rede (Network) do navegador ou da documentação da API, e cole em JSON para TypeScript. Uma resposta como esta:

{
  "id": 1024,
  "name": "Ada",
  "tags": ["admin", "beta"],
  "address": { "city": "London", "zip": "N1 9GU" },
  "orders": [
    { "order_id": "A-17", "total": 42, "shipped_at": "2026-09-01T10:00:00Z" },
    { "order_id": "A-18", "total": 18.5, "shipped_at": null, "coupon": "WELCOME" }
  ]
}

vira isto, com o Nome da raiz definido como User:

export interface Address {
  city: string;
  zip: string;
}

export interface Order {
  order_id: string;
  total: number;
  shipped_at: string | null;
  coupon?: string;
}

export interface User {
  id: number;
  name: string;
  tags: string[];
  address: Address;
  orders: Order[];
}

Repare no que o gerador deduziu a partir dos dois pedidos: shipped_at era null em um dos itens, então ficou string | null; coupon faltava em um dos itens, então ficou opcional. Os objetos aninhados viraram interfaces próprias com nome, e os itens de orders viraram Order. Tudo roda no navegador, então uma resposta com dados reais de clientes não é enviada a lugar nenhum.

Passo 2: use o tipo com o fetch

O jeito mais simples de ligar o tipo aos dados é uma pequena função auxiliar:

async function getJson<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return (await res.json()) as T;
}

const user = await getJson<User>('/api/users/1024');
user.address.city;   // string, checked by the compiler

O as T é uma promessa que você faz ao compilador, não uma verificação. Se o servidor mandar outra coisa, o TypeScript não vai perceber. Para APIs internas que você controla, essa troca muitas vezes é aceitável; para qualquer outra coisa, continue até o passo 4.

Passo 3: modele as partes complicadas

No JSONNo TypeScriptPor quê
Uma chave que às vezes faltacoupon?: stringOpcional: a propriedade pode não existir
Um valor que às vezes é nullshipped_at: string | nullAnulável: a propriedade existe, mas pode estar vazia
As duas coisasnote?: string | nullA API pode omitir a chave ou mandar null
Uma datastringO JSON não tem tipo de data; converta com new Date() onde precisar
Um ID grandestring se a API o mandar entre aspasNúmeros acima de 2^53 perdem precisão no JavaScript
Um array vazio no exemplounknown[]O exemplo não mostra como são os itens

Prefira unknown a any para tudo o que o exemplo não consegue informar: unknown obriga a uma verificação antes do uso, enquanto any desliga a verificação em silêncio. Um tipo gerado só é tão completo quanto o seu exemplo, então use uma resposta que inclua os campos opcionais, ou um array com vários itens variados.

Passo 4: valide em tempo de execução quando os dados não são seus

APIs de terceiros mudam, proxies devolvem páginas de erro em HTML e clientes antigos encontram servidores novos. Uma verificação em tempo de execução pega esses casos na fronteira, com um erro claro, em vez de lá no fundo da interface. Duas abordagens comuns:

  • JSON Schema: gere um schema a partir do mesmo exemplo com o gerador de JSON Schema e valide com uma biblioteca como o Ajv. O schema é neutro em relação à linguagem, então um backend em outra linguagem pode usar o mesmo arquivo.
  • Um validador pensado para TypeScript: bibliotecas como o Zod permitem descrever o formato uma vez e inferir dele o tipo TypeScript, de modo que o tipo e a verificação nunca se desencontram.

Em qualquer caso, valide uma única vez, no ponto onde os dados entram no seu programa, e deixe o resto do código confiar no tipo.

Quando a API muda

Tipos gerados uma vez vão ficando defasados conforme a API cresce. Guarde no repositório, ao lado dos tipos, o exemplo usado para gerá-los, para que a próxima pessoa consiga gerá-los de novo e ver num diff o que mudou. Se a API publica uma descrição OpenAPI, gerar os tipos a partir dela é mais confiável do que a partir de exemplos, porque ela lista cada campo opcional e cada valor possível, inclusive os que uma única resposta nunca mostra. Para APIs sem essa descrição, um validador em tempo de execução como os do passo 4 transforma uma divergência silenciosa num erro que você vê nos logs.

interface ou type?

Para dados de API, os dois funcionam. Interfaces podem ser estendidas e são mescladas quando declaradas duas vezes, o que algumas equipes preferem para tipos públicos; aliases de type também expressam uniões e mapped types. Escolha um estilo para o projeto e mantenha a consistência. O gerador produz os dois.

Outras linguagens a partir do mesmo exemplo

O mesmo exemplo de JSON também pode virar structs em Go, modelos Pydantic ou classes em C# no JSON para Tipos, o que ajuda a manter o frontend e o backend com o mesmo formato. Se o próprio exemplo estiver quebrado, o formatador de JSON mostra onde.

Perguntas Frequentes

Como gerar tipos TypeScript a partir de JSON?

Cole um exemplo real do JSON num conversor de JSON para TypeScript. Ele cria interfaces para cada objeto, com os campos opcionais e anuláveis detectados a partir do exemplo.

Por que res.json() tem o tipo any?

Porque o TypeScript não tem como saber, em tempo de compilação, o que um servidor vai mandar. Você mesmo dá um tipo ao resultado, com uma função auxiliar genérica ou um validador.

Um tipo TypeScript valida os dados?

Não. Os tipos são removidos quando o código é compilado. Para verificar dados em tempo de execução, use um validador de JSON Schema ou uma biblioteca como o Zod.

Devo usar opcional ou null?

Siga a API: use o ponto de interrogação quando a chave pode faltar, e uma união com null quando a chave vem presente com o valor null.

Como tipar datas que vêm no JSON?

Como string, já que o JSON não tem tipo de data. Converta-as em objetos Date onde precisar fazer contas com datas.