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);
}