Tipos de Erros em JavaScript
Em um bloco catch, podemos obter informações sobre um erro, que é representado por um objeto. Todos os erros lançados pelo interpretador de JavaScript fornecem um objeto do tipo Error, que possui uma série de propriedades:
message: a mensagem de erroname: o tipo de erro
Vale destacar que diferentes navegadores suportam mais algumas propriedades, mas o comportamento delas pode variar dependendo do navegador:
fileName: o nome do arquivo JavaScript onde o erro ocorreulineNumber: a linha no arquivo onde o erro ocorreucolumnNumber: a coluna na linha onde o erro ocorreustack: a pilha de erros
Vejamos como capturar os dados de um erro, por exemplo, ao chamar uma função inexistente:
try {
callSomeFunc();
} catch (error) {
console.log("Tipo de erro:", error.name);
console.log("Erro:", error.message);
}Saída do console:
Tipo de erro: ReferenceError Erro: callSomeFunc is not defined
Lista de Erros
Como vimos acima, o erro lançado pelo interpretador é do tipo Error. No entanto, ao chamar uma função inexistente, é lançado um erro do tipo ReferenceError. O tipo Error representa a categoria geral de erros. Por outro lado, existem tipos específicos de erros para determinadas situações:
EvalError: representa um erro que é lançado ao executar a função globaleval()RangeError: um erro é lançado se um parâmetro ou uma variável representa um número que está fora de um intervalo aceitávelReferenceError: um erro é lançado ao tentar acessar uma referência que não existeSyntaxError: representa um erro de sintaxeTypeError: um erro é lançado se o valor de uma variável ou parâmetro representa um tipo incorreto, ou ao tentar modificar um valor que não pode ser alteradoURIError: um erro é lançado ao passar valores incorretos às funções encodeURI() e decodeURI()AggregateError: fornece um erro que combina várias falhas ocorridas
Por exemplo, ao tentar atribuir um valor a uma constante pela segunda vez, um erro do tipo TypeError é lançado:
try {
const num = 9;
num = 7; // Tentativa de reatribuição
} catch (error) {
console.log(error.name); // TypeError
console.log(error.message); // Assignment to constant variable.
}Aplicando os Tipos de Erro
Ao lançar erros, podemos utilizar tipos de erro integrados. Por exemplo:
class Person {
constructor(name, age) {
if (age < 0) throw new Error("A idade deve ser positiva");
this.name = name;
this.age = age;
}
print() {
console.log(`Name: ${this.name} Age: ${this.age}`);
}
}
try {
const tom = new Person("Tom", -45);
tom.print();
} catch (error) {
console.log(error.message); // A idade deve ser positiva
}Aqui, o construtor da classe Person recebe valores para o nome e a idade da pessoa. Se a idade fornecida for negativa, é lançado um erro na forma de um objeto Error. A mensagem de erro é passada como parâmetro para o construtor de Error:
if (age < 0) throw new Error("A idade deve ser positiva");Assim, ao processar a exceção no bloco catch, podemos obter a mensagem de erro transmitida.
Todos os outros tipos de erro também aceitam uma mensagem de erro como primeiro parâmetro no construtor. Vamos lançar alguns tipos de erros:
class Person {
constructor(pName, pAge) {
const age = parseInt(pAge);
if (isNaN(age)) throw new TypeError("A idade deve ser um número");
if (age < 0 || age > 120) throw new RangeError("A idade deve ser maior que 0 e menor que 120");
this.name = pName;
this.age = age;
}
print() {
console.log(`Name: ${this.name} Age: ${this.age}`);
}
}
try {
const tom = new Person("Tom", -45);
} catch (error) {
console.log(error.message); // A idade deve ser maior que 0 e menor que 120
}
try {
const bob = new Person("Bob", "bla bla");
} catch (error) {
console.log(error.message); // A idade deve ser um número
}
try {
const sam = new Person("Sam", 23);
sam.print(); // Name: Sam Age: 23
} catch (error) {
console.log(error.message);
}Saída no console:
A idade deve ser maior que 0 e menor que 120 A idade deve ser um número Name: Sam Age: 23
Nesse exemplo, como a idade pode ser qualquer valor, inicialmente tentamos convertê-la em um número usando a função parseInt():
const age = parseInt(pAge);
if (isNaN(age)) throw new TypeError("A idade deve ser um número");Em seguida, com a função isNaN(age), verificamos se o número obtido é de fato um número. Se age não for um número, a função retorna verdadeiro. Portanto, é lançado um erro do tipo TypeError.
Depois verificamos se o número está dentro do intervalo aceitável. Se não estiver, lançamos um erro do tipo RangeError:
if (age < 0 || age > 120) {
throw new RangeError("A idade deve ser maior que 0 e menor que 120");
}Tratando Múltiplos Tipos de Erro
Quando executamos o mesmo código, podem ser lançados erros de diferentes tipos. E às vezes é necessário distinguir entre os tipos de tratamento. Neste caso, podemos verificar o tipo de erro que ocorreu. Por exemplo, o exemplo acima com a classe Person:
class Person {
constructor(pName, pAge) {
const age = parseInt(pAge);
if (isNaN(age)) throw new TypeError("A idade deve ser um número");
if (age < 0 || age > 120) throw new RangeError("A idade deve ser maior que 0 e menor que 120");
this.name = pName;
this.age = age;
}
print() {
console.log(`Name: ${this.name} Age: ${this.age}`);
}
}
try {
const tom = new Person("Tom", -45);
const bob = new Person("Bob", "bla bla");
} catch (error) {
if (error instanceof TypeError) {
console.log("Tipo de dado incorreto.");
} else if (error instanceof RangeError) {
console.log("Valor inaceitável");
}
console.log(error.message);
}Criando Seus Próprios Tipos de Erro
Não estamos limitados apenas aos tipos de erro integrados e podemos criar nossos próprios tipos de erro para situações específicas. Por exemplo:
class PersonError extends Error {
constructor(value, ...params) {
super(...params);
this.name = "PersonError";
this.argument = value;
}
}
class Person {
constructor(pName, pAge) {
const age = parseInt(pAge);
if (isNaN(age)) throw new PersonError(pAge, "A idade deve ser um número");
if (age < 0 || age > 120) throw new PersonError(pAge, "A idade deve ser maior que 0 e menor que 120");
this.name = pName;
this.age = age;
}
print() {
console.log(`Name: ${this.name} Age: ${this.age}`);
}
}
try {
const bob = new Person("Bob", "bla bla");
} catch (error) {
if (error instanceof PersonError) {
console.log("Erro do tipo Person. Valor incorreto:", error.argument);
}
console.log(error.message);
}Saída no console:
Erro do tipo Person. Valor incorreto: bla bla A idade deve ser um número
O tipo de erro PersonError é definido aqui, herdado da classe Error:
class PersonError extends Error {
constructor(value, ...params) {
super(...params);
this.name = "PersonError";
this.argument = value;
}
}No construtor, definimos uma propriedade adicional chamada argument, que armazenará o valor que causou o erro. Usamos o parâmetro value do construtor para obter esse valor. Além disso, redefinimos o nome do tipo de erro usando a propriedade this.name.
Na classe Person, usamos esse tipo, passando os valores correspondentes para o construtor de PersonError:
if (isNaN(age)) throw new PersonError(pAge, "A idade deve ser um número");
if (age < 0 || age > 120) throw new PersonError(pAge, "A idade deve ser maior que 0 e menor que 120");Depois, ao tratar a exceção, podemos verificar o tipo e, se for um PersonError, acessar sua propriedade argument:
catch (error) {
if (error instanceof PersonError) {
console.log("Erro do tipo Person. Valor incorreto:", error.argument);
}
console.log(error.message);
}