Aprenda / TypeScript do zero

FundamentosAula 7 de 1420 min0% da trilha

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>.

Quiz rápido

Por que buscarApi é genérica (buscarApi<T>) em vez de tipada só pra Pedido?
Por que Partial<Omit<Pedido, 'id'>> faz sentido pra uma função de atualização?