Todo programa que vai para produção vai encontrar situações inesperadas. O usuário digita um valor inválido. A API não responde. O arquivo não existe. O banco de dados cai. Nenhum desses cenários é um bug — são situações previsíveis que precisam ser tratadas com elegância.
Um código que não trata erros quebra silenciosamente, exibe mensagens técnicas para o usuário ou simplesmente para de funcionar. Um código bem escrito antecipa o que pode dar errado e age de forma controlada.
É isso que vamos aprender neste artigo.
O que é um erro em JavaScript?
Quando o JavaScript encontra um problema que não consegue resolver, ele lança um objeto de erro e interrompe a execução do código — a menos que você capture esse erro.
// Sem tratamento — o programa quebra aqui
const resultado = JSON.parse("isso não é um JSON válido");
console.log("Esta linha nunca executa.");
// SyntaxError: Unexpected token i in JSON at position 0
O erro se propaga para cima na pilha de chamadas até encontrar alguém que o trate — ou até travar o programa.
try e catch — capturando erros
A estrutura básica do tratamento de erros:
try {
// código que pode lançar um erro
const dados = JSON.parse("json inválido");
} catch (erro) {
// código executado SE um erro ocorrer
console.log("Algo deu errado:", erro.message);
}
console.log("O programa continua normalmente.");
// Algo deu errado: Unexpected token j in JSON at position 0
// O programa continua normalmente.
O bloco try envolve o código arriscado. Se qualquer linha dentro dele lançar um erro, a execução pula imediatamente para o catch — que recebe o objeto de erro como parâmetro.
O objeto de erro
O erro capturado é um objeto com propriedades úteis:
try {
null.propriedade; // TypeError
} catch (erro) {
console.log(erro.name); // TypeError
console.log(erro.message); // Cannot read properties of null
console.log(erro.stack); // Stack trace completo (onde o erro ocorreu)
}
As propriedades mais usadas são name (tipo do erro) e message (descrição legível).
Tipos de erros nativos
O JavaScript possui vários tipos de erros embutidos, cada um para uma situação diferente:
// ReferenceError — variável não existe
try {
console.log(variavelInexistente);
} catch (e) {
console.log(e.name); // ReferenceError
}
// TypeError — tipo errado para a operação
try {
null.metodo();
} catch (e) {
console.log(e.name); // TypeError
}
// SyntaxError — código ou dado mal formado
try {
JSON.parse("{chave sem aspas: valor}");
} catch (e) {
console.log(e.name); // SyntaxError
}
// RangeError — valor fora do intervalo permitido
try {
new Array(-1);
} catch (e) {
console.log(e.name); // RangeError
}
finally — executar sempre
O bloco finally executa sempre, independente de ter ocorrido um erro ou não. É ideal para código de limpeza que deve rodar de qualquer jeito:
function lerArquivo(nome) {
console.log(`Abrindo arquivo: ${nome}`);
try {
if (nome !== "dados.json") {
throw new Error("Arquivo não encontrado.");
}
console.log("Arquivo lido com sucesso!");
return "conteúdo do arquivo";
} catch (erro) {
console.log(`Erro: ${erro.message}`);
return null;
} finally {
// executa sempre — com ou sem erro
console.log("Fechando conexão com o arquivo.");
}
}
lerArquivo("dados.json");
// Abrindo arquivo: dados.json
// Arquivo lido com sucesso!
// Fechando conexão com o arquivo.
lerArquivo("outro.txt");
// Abrindo arquivo: outro.txt
// Erro: Arquivo não encontrado.
// Fechando conexão com o arquivo.
Na prática, finally é muito usado para fechar conexões com banco de dados, esconder indicadores de carregamento ou liberar recursos.
throw — lançando seus próprios erros
Você não precisa esperar o JavaScript lançar um erro — pode lançar os seus próprios com throw:
function dividir(a, b) {
if (b === 0) {
throw new Error("Divisão por zero não é permitida.");
}
return a / b;
}
try {
console.log(dividir(10, 2)); // 5
console.log(dividir(10, 0)); // lança erro
} catch (erro) {
console.log(`Erro capturado: ${erro.message}`);
}
// 5
// Erro capturado: Divisão por zero não é permitida.
Você pode lançar qualquer valor com throw — mas a convenção é sempre lançar um objeto Error para manter consistência.
Criando erros personalizados
Para sistemas maiores, é útil criar tipos de erro específicos estendendo a classe Error:
class ErroValidacao extends Error {
constructor(campo, mensagem) {
super(mensagem);
this.name = "ErroValidacao";
this.campo = campo;
}
}
class ErroAutenticacao extends Error {
constructor(mensagem) {
super(mensagem);
this.name = "ErroAutenticacao";
}
}
function validarEmail(email) {
if (!email.includes("@")) {
throw new ErroValidacao("email", `"${email}" não é um e-mail válido.`);
}
return true;
}
function autenticar(usuario, senha) {
if (senha.length < 6) {
throw new ErroAutenticacao("Senha deve ter pelo menos 6 caracteres.");
}
return true;
}
// Tratando erros de tipos diferentes
function processarLogin(email, senha) {
try {
validarEmail(email);
autenticar(email, senha);
console.log("Login realizado com sucesso!");
} catch (erro) {
if (erro instanceof ErroValidacao) {
console.log(`Campo inválido (${erro.campo}): ${erro.message}`);
} else if (erro instanceof ErroAutenticacao) {
console.log(`Falha de autenticação: ${erro.message}`);
} else {
console.log(`Erro inesperado: ${erro.message}`);
}
}
}
processarLogin("emailsemarroba.com", "123456");
// Campo inválido (email): "emailsemarroba.com" não é um e-mail válido.
processarLogin("user@email.com", "123");
// Falha de autenticação: Senha deve ter pelo menos 6 caracteres.
processarLogin("user@email.com", "senha123");
// Login realizado com sucesso!
Validação defensiva — errar cedo e com clareza
Um princípio de código limpo: valide as entradas no início da função e lance erros claros antes de continuar:
function criarUsuario({ nome, email, idade }) {
// Valide tudo antes de qualquer processamento
if (!nome || typeof nome !== "string") {
throw new Error("Nome é obrigatório e deve ser texto.");
}
if (!email || !email.includes("@")) {
throw new Error("E-mail inválido.");
}
if (!Number.isInteger(idade) || idade < 0 || idade > 130) {
throw new Error("Idade deve ser um número inteiro entre 0 e 130.");
}
// Só chegamos aqui se tudo estiver válido
return {
id: Math.random().toString(36).slice(2),
nome: nome.trim(),
email: email.toLowerCase(),
idade,
criadoEm: new Date().toISOString(),
};
}
try {
const usuario = criarUsuario({ nome: " Ana ", email: "ana@email.com", idade: 25 });
console.log(usuario);
} catch (erro) {
console.log(`Erro ao criar usuário: ${erro.message}`);
}
Erros silenciosos — o que evitar
Um anti-padrão perigoso é capturar erros e não fazer nada com eles:
// ❌ Nunca faça isso — engole o erro silenciosamente
try {
operacaoArriscada();
} catch (e) {
// silêncio total
}
// ❌ Também ruim — captura mas não trata corretamente
try {
operacaoArriscada();
} catch (e) {
console.log("deu erro"); // sem detalhes, sem ação
}
// ✅ Trate com intenção
try {
operacaoArriscada();
} catch (e) {
console.error(`[ERRO] ${e.name}: ${e.message}`);
// notificar sistema de monitoramento
// exibir mensagem útil ao usuário
// tentar uma alternativa (fallback)
}
Boas práticas de tratamento de erros
// ✅ 1. Seja específico — não trate tudo como erro genérico
catch (erro) {
if (erro instanceof TypeError) { /* ... */ }
if (erro instanceof RangeError) { /* ... */ }
}
// ✅ 2. Mensagens de erro úteis para o desenvolvedor
throw new Error(`Usuário com id ${id} não encontrado na base de dados.`);
// ✅ 3. Não lance erros para fluxo de controle normal
// Se "não encontrar" é esperado, retorne null — não lance erro
function buscarUsuario(id) {
const usuario = banco.find(u => u.id === id);
return usuario || null; // não lance erro para ausência esperada
}
// ✅ 4. Sempre use finally para limpeza de recursos
try {
conexao.abrir();
conexao.executar(query);
} catch (erro) {
console.error(erro);
} finally {
conexao.fechar(); // fecha sempre, com ou sem erro
}
Exemplo completo — sistema de cadastro
class ErroCadastro extends Error {
constructor(mensagem, campo = null) {
super(mensagem);
this.name = "ErroCadastro";
this.campo = campo;
}
}
function validarCadastro(dados) {
const { nome, email, senha, confirmacaoSenha } = dados;
if (!nome || nome.trim().length < 2) {
throw new ErroCadastro("Nome deve ter pelo menos 2 caracteres.", "nome");
}
if (!email || !email.includes("@")) {
throw new ErroCadastro("E-mail inválido.", "email");
}
if (!senha || senha.length < 8) {
throw new ErroCadastro("Senha deve ter pelo menos 8 caracteres.", "senha");
}
if (senha !== confirmacaoSenha) {
throw new ErroCadastro("As senhas não coincidem.", "confirmacaoSenha");
}
return true;
}
function cadastrarUsuario(dados) {
console.log("Iniciando cadastro...");
try {
validarCadastro(dados);
console.log(`Usuário "${dados.nome}" cadastrado com sucesso!`);
return { sucesso: true };
} catch (erro) {
if (erro instanceof ErroCadastro) {
console.log(`Erro no campo "${erro.campo}": ${erro.message}`);
} else {
console.log(`Erro inesperado: ${erro.message}`);
}
return { sucesso: false, erro: erro.message };
} finally {
console.log("Processo de cadastro finalizado.\n");
}
}
cadastrarUsuario({ nome: "A", email: "email@ok.com", senha: "12345678", confirmacaoSenha: "12345678" });
// Iniciando cadastro...
// Erro no campo "nome": Nome deve ter pelo menos 2 caracteres.
// Processo de cadastro finalizado.
cadastrarUsuario({ nome: "João", email: "emailinvalido", senha: "12345678", confirmacaoSenha: "12345678" });
// Iniciando cadastro...
// Erro no campo "email": E-mail inválido.
// Processo de cadastro finalizado.
cadastrarUsuario({ nome: "João", email: "joao@email.com", senha: "minhasenha", confirmacaoSenha: "minhasenha" });
// Iniciando cadastro...
// Usuário "João" cadastrado com sucesso!
// Processo de cadastro finalizado.
Tarefa para você
Construa uma função calcularMedia(notas) robusta que:
- Lance um erro se
notasnão for um array - Lance um erro se o array estiver vazio
- Lance um erro se alguma nota não for um número entre 0 e 10
- Retorne a média calculada se tudo estiver correto
- Trate todos os erros com mensagens claras e específicas
// Esperado:
calcularMedia([8, 7, 9]); // Média: 8.00
calcularMedia([]); // Erro: array vazio
calcularMedia("não sou um array"); // Erro: notas deve ser um array
calcularMedia([8, 15, 7]); // Erro: nota 15 está fora do intervalo
Conclusão
Neste artigo você aprendeu:
- O que acontece quando um erro não é tratado
- Como usar
try,catchefinally - As propriedades do objeto de erro
- Os tipos nativos de erro do JavaScript
- Como lançar erros com
throw - Como criar classes de erro personalizadas
- Validação defensiva e boas práticas
- O que nunca fazer com erros
No próximo artigo encerramos o Módulo 1 com uma revisão completa e um mini projeto prático — uma calculadora no console que usa tudo que aprendemos até aqui.
📚 Fontes e Referências
- MDN Web Docs — try...catch: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/try...catch
- MDN Web Docs — Error: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Error
- MDN Web Docs — throw: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/throw
- JavaScript.info — Error Handling: https://javascript.info/try-catch
- JavaScript.info — Custom Errors: https://javascript.info/custom-errors
- Eloquent JavaScript, Cap. 8 — Bugs and Errors: https://eloquentjavascript.net/08_error.html
- Clean Code — Robert C. Martin, Cap. 7: Error Handling (Alta Books)
- You Don't Know JS: ES6 & Beyond — Kyle Simpson: https://github.com/getify/You-Dont-Know-JS