Projeto guiado: tipando uma API
Junta interface, union, generics e utilitários numa função real de busca de dado.
Uma função buscarPedido que chama uma API de verdade, tipada do jeito que apareceria num projeto Next.js real — encadeando tudo que as 6 lições anteriores cobriram.
1. O formato dos dados (interface)
interface Pedido {
id: string;
status: "pendente" | "pago" | "cancelado";
total: number;
itens: ItemPedido[];
}
interface ItemPedido {
produto: string;
quantidade: number;
}2. O formato da resposta — sucesso ou erro (discriminated union + generics)
type ResultadoApi<T> =
| { sucesso: true; dado: T }
| { sucesso: false; erro: string };3. A função — genérica, reutilizável pra qualquer endpoint
async function buscarApi<T>(url: string): Promise<ResultadoApi<T>> {
try {
const res = await fetch(url);
if (!res.ok) {
return { sucesso: false, erro: `Erro ${res.status}` };
}
const dado: T = await res.json();
return { sucesso: true, dado };
} catch {
return { sucesso: false, erro: "Falha de rede" };
}
}4. Usando — o generic "vaza" o tipo certo pra quem chama
async function carregarPedido(id: string) {
const resultado = await buscarApi<Pedido>(`/api/pedidos/${id}`);
if (!resultado.sucesso) {
console.error(resultado.erro); // TS sabe que só "erro" existe aqui
return null;
}
return resultado.dado; // TS sabe que é um Pedido completo aqui, com autocomplete de .status, .itens...
}5. Formulário de edição — Partial entra aqui
async function atualizarPedido(id: string, mudancas: Partial<Omit<Pedido, "id">>) {
return buscarApi<Pedido>(`/api/pedidos/${id}`); // simplificado — na prática seria um PATCH
}
// só manda o que mudou, sem precisar reconstruir o Pedido inteiro:
atualizarPedido("1", { status: "pago" });Nenhuma dessas 5 partes existe isolada num projeto real — ResultadoApi<T> é o mesmo formato de "sucesso ou erro tipado" que aparece no Padrão API (lá, do lado do Rails, como ServiceResult); é o mesmo contrato, dos dois lados da rede.
Exercício
Escreva a assinatura de tipo de uma função criarPedido que recebe os dados de um pedido sem id e sem status (o backend define os dois), e devolve um ResultadoApi<Pedido>.