No TypeScript, uma promise tem o tipo Promise<T>, em que T é o tipo do valor com que ela resolve. Uma função que retorna uma promise declara isso no tipo de retorno, e o valor que você recebe de then ou de await tem o tipo T.
A mecânica das promises (estados, a fila de microtasks, o encadeamento) é JavaScript puro, coberta em promises em JavaScript. Esta página trata dos tipos.
Tipando new Promise e resolve
Quando new Promise é o valor de retorno de uma função com tipo de retorno declarado, o T vem dali. Sozinho, o TypeScript não infere T a partir das chamadas de resolve, e você recebe Promise<unknown>. Passe o argumento de tipo explicitamente:
Dois detalhes. resolve é tipado a partir de T, então resolve("42") em uma Promise<number> é erro de compilação. E resolve() sem argumento só é permitido quando T inclui void: em uma Promise<number> ele dá o erro TS2794 (Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?).
O lado do reject não é tipado. Não existe Promise<T, E>: uma promise pode rejeitar com qualquer valor.
then, catch e finally
Cada then retorna uma nova promise tipada pelo que o seu callback retorna. Se o callback retornar uma promise, o TypeScript a desembrulha, então você nunca recebe Promise<Promise<T>>.
O parâmetro do callback de catch é tipado como any na biblioteca padrão, não como unknown. Nada impede você de escrever e.message em um valor que pode ser uma string. Anote-o como unknown, como acima, e estreite antes de usar. try/catch em volta de await se sai melhor: com strict, a variável do catch já é unknown (veja tratamento de erros).
Promise.all retorna uma tupla
Dado um array literal de promises de tipos diferentes, Promise.all retorna uma promise de uma tupla, com cada posição mantendo o seu próprio tipo:
Valores que não são promises podem ser misturados e passam sem mudança. Promise.all rejeita assim que uma entrada rejeita, e os outros resultados se perdem. Promise.race resolve ou rejeita com a primeira a terminar e é tipada como a union das entradas; Promise.any resolve com o primeiro sucesso e rejeita com um AggregateError só se todas as entradas falharem.
Promise.allSettled e o tipo do resultado
Promise.allSettled espera todas as entradas e nunca rejeita. Cada resultado é um PromiseSettledResult<T>, uma union que você estreita com o campo status:
r.value só existe depois da verificação de status, porque a variante rejeitada não tem value. reason é any, pelo mesmo motivo que no catch. O filter usa um type predicate para que o array filtrado seja tipado como resultados cumpridos.
Envolvendo uma API de callback em uma promise
APIs mais antigas informam resultados por meio de um callback, muitas vezes no estilo do Node, (err, result) => void. Envolva-as uma vez em uma função que retorna uma promise tipada, e o resto do código pode usar await:
No Node, util.promisify faz isso para funções que seguem a convenção (err, result), e muitos módulos embutidos já têm versões com promises (node:fs/promises, node:timers/promises).
Erros comuns
- Retornar
Tonde a assinatura dizPromise<T>, ou o contrário. Uma função que não é async declarada comoPromise<User>precisa retornar uma promise; uma funçãoasyncretorna uma automaticamente. - Esquecer de tratar uma promise. Uma chamada como
save(user);semawait,thenoucatchcompila sem problemas, e uma rejeição vira uma unhandled rejection (que, por padrão, encerra um processo Node). A regrano-floating-promisesdo typescript-eslint pega esses casos. - Confiar no tipo de
.catch((e) => ...). Oedele éany. Anote-o comounknown. - Usar
new Promiseem volta de algo que já retorna uma promise. Basta retornar a promise existente ou fazerawaitnela.
Perguntas frequentes
O que é Promise<T> no TypeScript?
Promise<T> é o tipo de uma promise que resolve com um valor do tipo T. Uma função que retorna Promise<string> devolve uma promise cujo callback de then, ou o await, dá uma string. Uma promise que resolve sem valor é Promise<void>.
Como tipar new Promise no TypeScript?
Passe o argumento de tipo: new Promise<number>((resolve, reject) => ...). Sem ele, o TypeScript não consegue inferir o valor a partir das chamadas de resolve e o resultado é Promise<unknown>. Para uma promise que resolve sem nada, use new Promise<void>(resolve => ...) para que resolve() sem argumento seja permitido.
Qual é o tipo do erro no catch de uma promise?
any. O parâmetro reason do callback de catch é tipado como any na biblioteca padrão, porque qualquer coisa pode ser lançada ou rejeitada. Anote-o você mesmo como unknown (.catch((e: unknown) => ...)) e estreite com instanceof Error antes de usá-lo.
Como Promise.all funciona com tipos no TypeScript?
Promise.all sobre um array literal retorna um tipo tupla com um elemento por entrada, na ordem: Promise.all([getUser(), getCount()]) é Promise<[User, number]>, então a desestruturação dá a cada valor o seu próprio tipo. Sobre um array de um único tipo, T[], ele retorna Promise<T[]>.
Qual a diferença entre Promise.all e Promise.allSettled?
Promise.all rejeita assim que qualquer entrada rejeita. Promise.allSettled sempre resolve, com um array de objetos PromiseSettledResult<T>: { status: "fulfilled", value } ou { status: "rejected", reason }. Verifique status para estreitar cada um.