Tipar Promise e Async/Await no TypeScript
Promise e async/await são o coração do código assíncrono moderno. Tipar corretamente garante que o compilador pega erros antes de chegar em produção. Veja como usar Promise<T>, tipar
Por que isso é importante
Tipar Promise e Async/Await no TypeScript. Promise e async/await são o coração do código assíncrono moderno. Tipar corretamente garante que o compilador pega erros antes de chegar em produção. Veja como usar Promise
O Que É Promise<T> no TypeScript
Promise é um objeto que representa um valor que vai existir no futuro. No JavaScript puro, você cria uma Promise e ela resolve com qualquer coisa. No TypeScript, a gente coloca um tipo dentro do
Quando você escreve Promise
O mesmo vale pra async/await. Toda função marcada com async retorna uma Promise automaticamente. Se a função retorna string, o tipo real é Promise
A parte boa é que, na maioria dos casos, o TypeScript consegue inferir o tipo da Promise pelo valor de retorno. Mas quando você tá trabalhando com APIs externas, dados vindos do banco ou bibliotecas sem tipagem, declarar o tipo explicitamente salva horas de debug.
Passo a Passo: Tipando Promise e Async/Await
Vamos construir o conhecimento em etapas. Cada passo adiciona uma camada de segurança ao seu código assíncrono.
- Passo 1 - Crie uma Promise tipada: Use new Promise
((resolve, reject) => { ... }). O resolve só vai aceitar valores do tipo que você declarou. Se declarou Promise , passar uma string no resolve causa erro de compilação. - Passo 2 - Tipe o retorno de funções async: Declare o tipo de retorno da função: async function buscarUsuario(): Promise
. Isso garante que o await sempre entrega o tipo certo. Se a implementação retornar algo diferente, o compilador avisa. - Passo 3 - Use generics quando o tipo varia: Crie funções genéricas como async function fetchData
(url: string): Promise . Assim, cada chamada define o tipo esperado: fetchData ('/api/produtos'). - Passo 4 - Tipe Promise.all e Promise.race: Promise.all recebe um array de Promises e retorna uma tupla. O TypeScript infere os tipos individualmente: const [user, posts] = await Promise.all([getUser(), getPosts()]). Cada variável recebe o tipo correto.
- Passo 5 - Trate erros com try/catch tipado: O catch no TypeScript recebe unknown por padrão. Antes de usar o erro, verifique com instanceof Error. Isso garante acesso seguro a message e stack.
- Passo 6 - Evite Promise
e Promise quando não faz sentido: Promise desliga a checagem. Promise só serve quando a função não retorna dado útil, como salvar um log. Nos outros casos, tipe o retorno com precisão.
Exemplos Práticos de Promise Tipada
Hora de ver código. Cada exemplo mostra um cenário real que você vai encontrar no dia a dia.
Promise Básica Tipada
// Promise que resolve com string
const promessaTexto: Promise<string> = new Promise((resolve) => {
setTimeout(() => {
resolve("Dados carregados");
}, 1000);
});
// Promise que resolve com número
const promessaNumero: Promise<number> = new Promise((resolve) => {
resolve(42);
});
// Usando o resultado
promessaTexto.then((texto) => {
console.log(texto.toUpperCase()); // OK, texto é string
});
Funções Async com Tipo de Retorno
interface Usuario {
id: number;
nome: string;
email: string;
}
// Tipo de retorno explícito: Promise<Usuario>
async function buscarUsuario(id: number): Promise<Usuario> {
const response = await fetch(`/api/usuarios/${id}`);
const data: Usuario = await response.json();
return data;
}
// O TypeScript sabe que 'user' é do tipo Usuario
const user = await buscarUsuario(1);
console.log(user.nome); // autocomplete funciona
console.log(user.email); // tudo tipado
Função Genérica para Fetch Tipado
// Função genérica que aceita qualquer tipo
async function fetchAPI<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>;
}
interface Produto {
id: number;
nome: string;
preco: number;
}
interface Pedido {
id: number;
total: number;
itens: Produto[];
}
// Cada chamada define o tipo retornado
const produto = await fetchAPI<Produto>("/api/produtos/1");
console.log(produto.preco); // tipo number
const pedido = await fetchAPI<Pedido>("/api/pedidos/1");
console.log(pedido.itens); // tipo Produto[]
Promise.all com Tipagem Automática
async function getUser(): Promise<Usuario> {
return { id: 1, nome: "Maria", email: "maria@email.com" };
}
async function getPosts(): Promise<string[]> {
return ["Post 1", "Post 2", "Post 3"];
}
async function getStats(): Promise<number> {
return 150;
}
// Promise.all infere uma tupla: [Usuario, string[], number]
const [user, posts, stats] = await Promise.all([
getUser(),
getPosts(),
getStats(),
]);
console.log(user.nome); // string
console.log(posts.length); // number
console.log(stats); // number
// Promise.allSettled - retorna status de cada Promise
const resultados = await Promise.allSettled([
getUser(),
getPosts(),
]);
resultados.forEach((r) => {
if (r.status === "fulfilled") {
console.log(r.value); // tipo inferido corretamente
} else {
console.log(r.reason); // erro
}
});
Try/Catch com Tipagem Segura
// O catch recebe 'unknown' por padrão no TypeScript
async function carregarDados(): Promise<Usuario | null> {
try {
const response = await fetch("/api/usuarios/1");
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
}
return await response.json();
} catch (erro: unknown) {
// Precisa verificar o tipo antes de usar
if (erro instanceof Error) {
console.error("Mensagem:", erro.message);
console.error("Stack:", erro.stack);
} else {
console.error("Erro desconhecido:", erro);
}
return null;
}
}
// Padrão Result para evitar try/catch espalhado
type Result<T> =
| { ok: true; data: T }
| { ok: false; error: string };
async function fetchSeguro<T>(url: string): Promise<Result<T>> {
try {
const res = await fetch(url);
const data: T = await res.json();
return { ok: true, data };
} catch (e) {
return { ok: false, error: e instanceof Error ? e.message : "Erro" };
}
}
const resultado = await fetchSeguro<Produto>("/api/produtos/1");
if (resultado.ok) {
console.log(resultado.data.preco); // tipado como Produto
} else {
console.log(resultado.error); // string
}
O padrão Result é ouro puro pra projetos grandes. Em vez de try/catch em todo lugar, você centraliza o tratamento e o TypeScript te obriga a checar se deu certo antes de usar o dado. Isso elimina uma categoria inteira de bugs.
Erros Comuns com Promise no TypeScript
Armadilhas que pegam até dev experiente
Esquecer o await e trabalhar com a Promise em vez do valor: se você faz const user = buscarUsuario(1) sem await, user é Promise
Usar Promise
Não tratar o catch corretamente: o parâmetro do catch é unknown no modo strict. Tentar acessar erro.message direto causa erro de compilação. Sempre faça instanceof Error antes.
Misturar .then() com async/await sem necessidade: escolha um padrão e siga. Misturar os dois deixa o código confuso e dificulta a tipagem. Async/await é mais legível na maioria dos casos.
Ignorar Promise.allSettled quando precisa de resiliência: Promise.all rejeita tudo se uma Promise falha. Se você quer resultados parciais, use Promise.allSettled e verifique o status de cada resultado.
Checklist de Promise Tipada
- Funções async têm tipo de retorno explícito: Promise
- Interfaces criadas para dados vindos de APIs externas
- Promise.all com desestruturação tipada nos resultados
- Catch usa unknown e verifica instanceof Error
- Nenhum Promise
no código — trocado por tipos concretos - Funções genéricas para fetch reutilizável com tipagem
- Promise.allSettled usado quando precisa de resiliência
- Padrão Result implementado para tratamento centralizado de erros
Domine Código Assíncrono com TypeScript
Tipar Promises é o que separa um projeto amador de um profissional. No CrazyStack, todo o backend é construído com funções async tipadas, padrão Result e tratamento de erros sólido. Você não aprende teoria: constrói um SaaS completo com Node.js, React e TypeScript do zero ao deploy.
Se você quer escrever código assíncrono que não quebra em produção, esse é o caminho.