Erros personalizados, estendendo o erro

Quando desenvolvemos alguma coisa, muitas vezes precisamos de nossas próprias classes de erro para refletir coisas específicas que podem errar em nossas tarefas. Para erros nas operações de rede, podemos precisar HttpError, para operações de banco de dados , para operações de pesquisa NotFoundError, etc.

Os nossos erros devem suportar propriedades de erro básicas, comomessage,namee, de preferência,stack. Mas eles também podem ter outras propriedades, por exemplo, objetosHttpErrorPode ter uma propriedadestatusCodecom um valor como ou403ou .

javascript permite que você use throw Com qualquer argumento, então tecnicamente nossas aulas de erro personalizadas não precisam herdar de Error. Mas se herdarmos, então é possível usar obj instanceof Error para identificar erros de objetos. Então é melhor herdar dele.

Como o aplicativo cresce, nossos próprios erros formam naturalmente uma hierarquia. Por exemplo, HttpTimeoutError pode herdar de HttpError e assim por diante.

Erro de extensão

Como um exemplo, considere uma função readUser(json) Que deve ler JSON com os dados do usuário.

Aqui está um exemplo de como um válido:

let json = `{ "name": "John", "age": 30 }`;

Internamente, usaremos JSON.parse. Se você receber mal formado, então derrame SyntaxError. Mas mesmo se está sintaticamente correto, isso não significa que seja um usuário válido, certo? Você pode perder os dados necessários. Por exemplo, você pode não ter propriedades de nome e idade que são essenciais para nossos usuários.

Nossa função readUser(json) não só lerá JSON, mas verificará (“” Validar “) os dados. Se não houver campos obrigatórios, ou o formato estiver incorreto, ele será um erro. E isso não é um” syntaxError “, porque os dados estão sintaticamente corretos, mas outro tipo de erro. Vamos chamá-lo ValidationError e criar uma classe para ela. Um erro desse tipo também deve trazer as informações sobre o campo mineral.

Nossa classe ValidationError deve herdar da classe incorporada Error

esse tipo é incorporado, mas aqui está o seu código aproximado para que possamos entender o que estamos estendendo:

Id = “5E6E5955144”>

Agora vamos herdar ValidationError e prove em ação:

class ValidationError extends Error { constructor(message) { super(message); // (1) this.name = "ValidationError"; // (2) }}function test() { throw new ValidationError("Vaya!");}try { test();} catch(err) { alert(err.message); // Vaya! alert(err.name); // ValidationError alert(err.stack); // una lista de llamadas anidadas con números de línea para cada una}

nota: na linha (1) Nós chamamos o Pai Builder. JavaScript requer que chamemos super no construtor filho, por isso é obrigatório. O construtor pai define a propriedade message.

O construtor principal também define a propriedade name no "Error", portanto, na linha (2) redefina para o valor correto.

Tente usá-lo em readUser(json)

class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; }}// Usofunction readUser(json) { let user = JSON.parse(json); if (!user.age) { throw new ValidationError("Sin campo: age"); } if (!user.name) { throw new ValidationError("Sin campo: name"); } return user;}// Ejemplo de trabajo con try..catchtry { let user = readUser('{ "age": 25 }');} catch (err) { if (err instanceof ValidationError) { alert("Dato inválido: " + err.message); // Dato inválido: sin campo: nombre } else if (err instanceof SyntaxError) { // (*) alert("Error de sintaxis JSON: " + err.message); } else { throw err; // error desconocido, vuelva a lanzarlo (**) }}

O bloco try..catch No código anterior lida com a nossa ValidationError como o SyntaxError Built-in JSON.parse

Note Como usamos para verificar o tipo de erro específico no (*).

Também poderíamos olhar para err.name, assim:

// ...// en lugar de (err instanceof SyntaxError)} else if (err.name == "SyntaxError") { // (*)// ...

a versão instanceof é muito melhor, porque em o futuro vamos estender Faremos subtipos dele, como PropertyRequiredError. E controle instanceof continuará a trabalhar para novas classes herdadas. Então isso vai para o futuro.

Também é importante que, se catch Encontre um erro desconhecido, então ele a re-jogá-lo na linha

. O blococatchSabe apenas como manipular erros de validação e sintaxe, outros tipos (devido a um erro tipográfico no código ou outros estranhos) deve falhar.

adicional herança

classe ValidationError é muito genérico. Muitas coisas podem dar errado.A propriedade pode estar ausente ou pode estar em um formato incorreto (como um valor de string para age). Vamos fazer uma classe mais concreta PropertyRequiredError, exatamente para propriedades ausentes. Ele terá informações adicionais sobre a propriedade ausente.

class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; }}class PropertyRequiredError extends ValidationError { constructor(property) { super("Sin propiedad: " + property); this.name = "PropertyRequiredError"; this.property = property; }}// Usofunction readUser(json) { let user = JSON.parse(json); if (!user.age) { throw new PropertyRequiredError("age"); } if (!user.name) { throw new PropertyRequiredError("name"); } return user;}// Ejemplo de trabajo con try..catchtry { let user = readUser('{ "age": 25 }');} catch (err) { if (err instanceof ValidationError) { alert("Dato inválido: " + err.message); // Dato inválido: Sin propiedad: name alert(err.name); // PropertyRequiredError alert(err.property); // name } else if (err instanceof SyntaxError) { alert("Error de sintaxis JSON: " + err.message); } else { throw err; // error desconocido, vuelva a lanzarlo }}

a nova classe PropertyRequiredError é fácil de usar: só precisamos passar o nome da propriedade: new PropertyRequiredError(property) . O message é gerado pelo construtor.

Por favor, note que this.name no construtor PropertyRequiredError é atribuído manualmente novamente. Isso pode se tornar um pouco tedioso: Atribuir this.name = <class name> em cada classe de erro personalizada. Podemos evitá-lo, tornando nosso próprio “erro básico” que atribui this.name = this.constructor.name. E, em seguida, herda todos os nossos erros personalizados.

Vamos chamá-lo MyError Aqui está o código com MyError e outras classes personalizadas, erro simplificado:

class MyError extends Error { constructor(message) { super(message); this.name = this.constructor.name; }}class ValidationError extends MyError { }class PropertyRequiredError extends ValidationError { constructor(property) { super("sin propiedad: " + property); this.property = property; }}// name es incorrectoalert( new PropertyRequiredError("campo").name ); // PropertyRequiredError

Agora os erros personalizados são muito mais curtos, especialmente ValidationError, já que excluímos a linha "this.name = ..." no construtor.

incorporado de exceções

a finalidade da função readUser No código anterior é “Leia os dados do usuário”. Pode haver diferentes tipos de erros no processo. Neste momento temos SyntaxError e ValidationError, mas no futuro a função readUser pode Cresça e provavelmente gere outros tipos de erros.

O código que chama readUser deve lidar com esses erros. Neste momento, usa vários if no bloco catch, que confirma a classe e lidar com os erros conhecidos e lançar os estranhos novamente.

o esquema é assim:

try { ... readUser() // la fuente potencial de error ...} catch (err) { if (err instanceof ValidationError) { // manejar errores de validación } else if (err instanceof SyntaxError) { // manejar errores de sintaxis } else { throw err; // error desconocido, vuelva a lanzarlo }}

No código anterior, podemos ver dois tipos de erros, mas pode haver mais.

Se a função readUser gera vários tipos de erros, então devemos Pergunte-se: Nós realmente queremos verificar todos os tipos de erro um por um a cada vez?

Muitas vezes, a resposta é “não”: nós gostaríamos de ser “um nível acima de tudo isso”. Nós só queremos saber se houve um “erro de leitura de dados”: por que aconteceu exatamente é quase irrelevante (a mensagem de erro descreve). Ou melhor ainda, gostaríamos de ter uma maneira de obter os detalhes do erro, Mas apenas se necessário.

A técnica que descrevemos aqui é chamada “Exceções de embalagem”.

  1. vamos criar uma nova classe ReadError representar um erro genérico de “leitura de dados”.
  2. a função readUser detectará os erros de leitura de dados que ocorrem dentro dela, como ValidationError e SyntaxError e gerar um ReadError
  3. o objeto ReadError Manterá a referência de erro original em sua propriedade cause

Então, o código que chama readUser só terá que verificar ReadError, nem todos os tipos de erros de leitura D Atos. E se você precisar de mais detalhes de um erro, poderá verificar sua propriedade cause.

Aqui está o código que define ReadError e demonstra seu uso em readUser e try..catch:

class ReadError extends Error { constructor(message, cause) { super(message); this.cause = cause; this.name = 'ReadError'; }}class ValidationError extends Error { /*...*/ }class PropertyRequiredError extends ValidationError { /* ... */ }function validateUser(user) { if (!user.age) { throw new PropertyRequiredError("age"); } if (!user.name) { throw new PropertyRequiredError("name"); }}function readUser(json) { let user; try { user = JSON.parse(json); } catch (err) { if (err instanceof SyntaxError) { throw new ReadError("Error de sintaxis", err); } else { throw err; } } try { validateUser(user); } catch (err) { if (err instanceof ValidationError) { throw new ReadError("Error de validación", err); } else { throw err; } }}try { readUser('{json malo}');} catch (e) { if (e instanceof ReadError) { alert(e); // Error original: SyntaxError: inesperado token b en JSON en la posición 1 alert("Error original: " + e.cause); } else { throw e; }}

No código anterior, readUser funciona Exatamente como se descrevem: detecta erros de sintaxe e validação e lança os erros ReadError (erros desconhecidos são gerados como de costume).

Então, o código externo verifica instanceof ReadError e é isso. Não é necessário listar todos os tipos de erro possíveis.

O foco é chamado de “exceções de embalagem”, porque tomamos exceções “baixo nível” e “ajuste” em ReadError O que é mais abstrato. É amplamente utilizado em programação orientada a objetos.

Resumo

  • Podemos herdar de Error e outras classes de erro gentilmente embutidas. Nós só precisamos cuidar da propriedade name e não se esqueça de chamar super.
  • podemos usar para verificar erros específicos. Também funciona com herança.Mas às vezes temos um objeto de erro que vem de uma biblioteca de terceiros e não há maneira fácil de obter sua classe. Em seguida, a propriedade name pode ser usada para esses controles.
  • Exceções O pacote é uma técnica generalizada: uma função manipula exceções de baixo nível e cria erros de alto nível em Lugar de vários erros de baixo nível. As exceções de baixo nível são às vezes convertidas em propriedades desse objeto como err.cause nos exemplos anteriores, mas isso não é estritamente necessário.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *