Por Que Usar TypeScript com Promises?
Promises são uma maneira popular de lidar com operações assíncronas no JavaScript, como requisições HTTP, leitura de arquivos e temporizadores. Ao usar TypeScript com Promises, você pode garantir que os valores retornados e os erros tratados sejam fortemente tipados, aumentando a segurança do código. O TypeScript ajuda a evitar erros comuns, como retornos inesperados ou tipos incorretos, fornecendo feedback imediato durante o desenvolvimento.
O Que é uma Promise?
Uma Promise é um objeto que representa a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante. Uma Promise pode estar em três estados: pending
(pendente), fulfilled
(concluída) ou rejected
(rejeitada).
Exemplo básico de uma Promise:
const minhaPromise = new Promise((resolve, reject) => {
const sucesso = true;
if (sucesso) {
resolve('Operação bem-sucedida!');
} else {
reject('Ocorreu um erro.');
}
});
minhaPromise
.then((resultado) => console.log(resultado))
.catch((erro) => console.log(erro));
Aqui, a Promise chama resolve
se a operação for bem-sucedida, ou reject
se houver um erro. O TypeScript, ao combinar tipagem com Promises, garante que os tipos retornados e erros tratados sejam coerentes.
Criando e Tipando Promises no TypeScript
Ao trabalhar com Promises no TypeScript, é essencial definir o tipo do valor que será retornado quando a Promise for resolvida. Isso garante que o tipo de dado retornado seja corretamente inferido em todas as partes do código.
1. Promises com Tipagem Básica
Veja como criar e tipar uma Promise que retorna um valor numérico:
function obterNumero(): Promise<number> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(42);
}, 1000);
});
}
obterNumero()
.then((numero) => console.log(`Número recebido: ${numero}`))
.catch((erro) => console.log(`Erro: ${erro}`));
Aqui, a função obterNumero
retorna uma Promise<number>
, o que garante que a função assíncrona sempre retornará um número. O TypeScript verificará o tipo no then
, garantindo que o numero
seja tratado corretamente como number
.
2. Promises com Tipagem de Erros
Também podemos garantir que os erros tratados em uma Promise sejam de tipos específicos. Veja um exemplo de como tipar o valor de erro:
function buscarUsuario(id: number): Promise<string> {
return new Promise((resolve, reject) => {
if (id === 1) {
resolve('Usuário encontrado: Mauro Souza');
} else {
reject(new Error('Usuário não encontrado.'));
}
});
}
buscarUsuario(2)
.then((usuario) => console.log(usuario))
.catch((erro: Error) => console.log(erro.message));
Aqui, o catch
está tipado para garantir que o erro seja uma instância de Error
, o que permite acessar a propriedade message
de forma segura.
Funções Assíncronas com async/await
no TypeScript
O TypeScript oferece excelente suporte para funções assíncronas usando async
e await
. Com await
, você pode escrever código assíncrono de forma mais linear, sem a necessidade de encadeamentos complexos de then
e catch
.
1. Usando async/await
com Tipagem
Veja como criar uma função assíncrona que retorna uma Promise tipada no TypeScript:
async function obterDados(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => resolve('Dados recebidos!'), 2000);
});
}
async function processarDados() {
try {
const dados = await obterDados();
console.log(dados);
} catch (erro) {
console.error(erro);
}
}
processarDados();
Nesse exemplo, a função obterDados
retorna uma Promise<string>
, e a função processarDados
usa await
para esperar a resolução dessa Promise. O TypeScript garante que o valor retornado seja do tipo string
e que o catch
trate o erro corretamente.
2. Encadeando Funções Assíncronas
Você pode encadear múltiplas funções assíncronas e garantir que todos os valores estejam corretamente tipados:
async function primeiraOperacao(): Promise<number> {
return 10;
}
async function segundaOperacao(valor: number): Promise<string> {
return `Resultado: ${valor * 2}`;
}
async function executarOperacoes() {
try {
const numero = await primeiraOperacao();
const resultado = await segundaOperacao(numero);
console.log(resultado);
} catch (erro) {
console.error(erro);
}
}
executarOperacoes();
Neste exemplo, o TypeScript garante que a função segundaOperacao
receba o valor numérico correto de primeiraOperacao
e retorne uma string tipada. A função executarOperacoes
lida com a execução sequencial dessas operações assíncronas.
Tratamento de Erros em Promises com TypeScript
O tratamento de erros é uma parte importante ao trabalhar com Promises. O TypeScript permite tipar os erros e garantir que os tipos de erro sejam tratados adequadamente. Isso é especialmente útil ao trabalhar com APIs que podem retornar diferentes tipos de erro.
1. Tratando Erros com try/catch
Em uma função assíncrona, o try/catch
é uma maneira comum de tratar erros. Veja como isso funciona com TypeScript:
async function buscarDados(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Erro ao buscar dados')), 1000);
});
}
async function processar() {
try {
const dados = await buscarDados();
console.log(dados);
} catch (erro: any) {
if (erro instanceof Error) {
console.error(erro.message);
}
}
}
processar();
No catch
, o TypeScript permite que você verifique se o erro é uma instância de Error
antes de acessar suas propriedades, garantindo segurança ao manipular o erro.
2. Tratamento Global de Erros
Em projetos maiores, pode ser útil criar uma função global para tratar erros de Promises, garantindo consistência na manipulação de erros:
function tratarErro(erro: unknown) {
if (erro instanceof Error) {
console.error('Erro:', erro.message);
} else {
console.error('Erro desconhecido:', erro);
}
}
async function executarComErro() {
try {
throw new Error('Erro simulado!');
} catch (erro) {
tratarErro(erro);
}
}
executarComErro();
Neste exemplo, a função tratarErro
lida com erros de forma genérica, garantindo que qualquer erro seja tratado adequadamente, independentemente do tipo.
Boas Práticas ao Usar Promises com TypeScript
- Defina Tipos de Retorno para Promises: Sempre que possível, defina explicitamente o tipo de retorno das Promises para garantir a previsibilidade do código.
- Use
async/await
para Fluxos Mais Claros: Prefiraasync/await
para operações assíncronas complexas, pois melhora a legibilidade do código em comparação ao encadeamento dethen
ecatch
. - Verifique o Tipo de Erros: Use
instanceof
para garantir que os erros capturados sejam do tipo correto antes de manipulá-los. - Evite o Uso de
any
: Tipar erros comoany
deve ser evitado. Useunknown
para representar erros desconhecidos e verifique seus tipos adequadamente. - Trate Erros de Forma Global: Considere criar funções globais para lidar com erros, garantindo consistência em toda a aplicação.
Conclusão
Combinar Promises com TypeScript oferece uma maneira segura e eficiente de lidar com operações assíncronas. A tipagem forte garante que os valores retornados sejam previsíveis e que os erros sejam tratados corretamente. Usando async/await
e tipando adequadamente as Promises, você pode melhorar a legibilidade do código e evitar erros em tempo de execução, tornando o desenvolvimento mais confiável e produtivo.