Back

Como Tipar Respostas de API em TypeScript

Como Tipar Respostas de API em TypeScript

Todo desenvolvedor frontend já passou por isso: você busca dados de uma API, acessa uma propriedade e recebe undefined em tempo de execução — mesmo que o TypeScript nunca tenha reclamado. O problema não está no seu código. É que o sistema de tipos do TypeScript opera apenas em tempo de compilação. Ele não tem ideia do que sua API realmente retorna.

Este artigo aborda padrões práticos para tipar respostas de API em TypeScript, desde a definição de interfaces básicas até validação em tempo de execução e geração de tipos orientada por contratos.

Pontos-Chave

  • O sistema de tipos do TypeScript opera apenas em tempo de compilação — ele não pode validar a forma real dos dados retornados por uma API em tempo de execução.
  • Use interfaces ou type aliases com uniões discriminadas para definir formas de resposta claras e esperadas.
  • Um wrapper genérico de fetch mantém os pontos de chamada limpos e torna os tipos esperados explícitos.
  • Para dados não confiáveis ou externos, use bibliotecas de validação em tempo de execução como Zod, Valibot ou ArkType para fechar a lacuna entre tipos em tempo de compilação e respostas do mundo real.
  • Quando existir uma especificação OpenAPI, gere seus tipos a partir dela para manter os contratos de frontend e backend sincronizados.

Por Que o TypeScript Não Pode Protegê-lo em Tempo de Execução

Quando você chama response.json(), o TypeScript normalmente trata o resultado como unknown (ou às vezes any, dependendo das tipagens do ambiente). Isso significa que você pode convertê-lo para o que quiser — e o compilador confiará completamente em você.

// ❌ TypeScript confia cegamente nesta conversão
const data = (await response.json()) as User
console.log(data.email) // Pode ser undefined em tempo de execução

Este é o limite central a entender: tipar respostas de API no TypeScript oferece segurança em tempo de compilação, mas não valida o JSON real que sua API retorna. Se a forma mudar, o TypeScript não saberá.

Definindo Tipos para Formas de Resposta Esperadas

O primeiro passo é dar estrutura ao que você espera. Use interfaces ou type aliases para descrever a forma da resposta:

interface User {
  id: string
  name: string
  email: string
}

Para APIs consistentes, um wrapper genérico lida com estados de sucesso e erro de forma limpa:

type ApiResponse<T> =
  | { status: "ok"; data: T }
  | { status: "error"; message: string }

Este padrão de união discriminada é mais transparente do que um único tipo com muitos campos opcionais. Quando você verifica status, o TypeScript reduz o tipo automaticamente — nenhum guard extra necessário.

Tipando Respostas de Fetch com um Wrapper Genérico

Tipar respostas de fetch no TypeScript fica mais limpo com um helper reutilizável:

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

// Uso
const user = await apiFetch<User>("/api/users/1")

Isso mantém seus pontos de chamada limpos enquanto torna a forma esperada explícita. O mesmo padrão funciona com Axios ou qualquer outro cliente HTTP.

Validação em Tempo de Execução: Fechando a Lacuna

Asserções de tipo como as User são uma promessa ao compilador, não uma garantia. Para dados externos não confiáveis — APIs de terceiros, conteúdo gerado por usuários ou qualquer resposta que você não controla totalmente — você precisa de validação em tempo de execução.

Zod é a opção mais amplamente usada. Você define um schema, analisa a resposta e obtém um resultado totalmente tipado:

import { z } from "zod"

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})

type User = z.infer<typeof UserSchema> // Derivado do schema

const data = await response.json()
const user = UserSchema.parse(data) // Lança erro se a forma estiver errada

O principal benefício: seus tipos TypeScript e sua validação em tempo de execução permanecem sincronizados porque derivam da mesma fonte de verdade.

Alternativas que vale conhecer:

  • Valibot — bundle menor, API similar ao Zod
  • ArkType — altamente performático, sintaxe nativa do TypeScript

Use .parse() quando quiser lançar erro em dados inválidos, ou .safeParse() quando quiser lidar com erros graciosamente sem exceções.

Tipagem Orientada por Contratos com OpenAPI

Se sua API tem uma especificação OpenAPI, você pode gerar tipos TypeScript automaticamente usando openapi-typescript:

npx openapi-typescript ./api-spec.yaml -o ./types/api.d.ts

Esta abordagem mantém seus tipos sincronizados com a documentação da sua API. É complementar à validação em tempo de execução — tipos gerados lidam com a camada de tempo de compilação, enquanto Zod ou Valibot lidam com a camada de tempo de execução.

Escolhendo a Abordagem Certa

CenárioPadrão Recomendado
API interna que você controlaInterface + asserção de tipo
Contrato compartilhado com backendTipos gerados por OpenAPI
API de terceiros ou não confiávelValidação de schema com Zod/Valibot
Ambas camadas de segurança necessáriasTipos gerados + schema em tempo de execução

Conclusão

O TypeScript oferece estrutura e autocompletar, mas não pode validar o que chega pela rede. Para APIs que você confia, uma interface bem definida e um wrapper de fetch tipado são suficientes. Para dados externos ou imprevisíveis, combine seus tipos com um validador em tempo de execução como Zod. Quando você tem uma especificação de API, gere seus tipos a partir dela. Essas abordagens não competem — elas funcionam melhor juntas.

Perguntas Frequentes

Não. A palavra-chave 'as' é uma asserção de tipo, não uma verificação em tempo de execução. Ela diz ao compilador para tratar um valor como um tipo específico, mas não faz nada para verificar os dados reais. Se a forma da resposta da API diferir do que você afirmou, você terá erros em tempo de execução que o TypeScript não pode capturar.

Use Zod ou uma biblioteca similar de validação em tempo de execução sempre que consumir dados de uma fonte que você não controla totalmente, como APIs de terceiros ou endpoints públicos. Se você possui tanto o frontend quanto o backend e tem forte confiança na forma da resposta, uma interface simples com asserção de tipo geralmente é suficiente.

Sim, e esta combinação é recomendada para máxima segurança. Tipos gerados por OpenAPI fornecem autocompletar e verificação de tipos em tempo de compilação, enquanto schemas Zod validam os dados reais da resposta em tempo de execução. Juntos, eles cobrem ambas as camadas do espectro de segurança de tipos.

O método response.json() da Fetch API retorna Promise de any por padrão no TypeScript. Isso significa que o compilador não aplicará nenhuma verificação de tipo no resultado, a menos que você explicitamente afirme ou valide. Algumas equipes sobrescrevem isso com tipagens mais estritas que retornam unknown, forçando validação explícita.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay