🚩 Exceções

Uma exceção é um evento que altera o fluxo normal de execução do programa. Em C, por exemplo, uma exceção pode envolver o acesso a uma região inválida de memória com um ponteiro.

Nos sistemas computacionais que usamos diariamente, exceções podem envolver também tentativas de acesso a um recurso que não está disponível no momento, como um servidor de banco de dados que está fora do ar.

Quando não tratadas devidamente, as exceções podem interromper a execução do programa, gerando um comportamento indesejado. É esperado que o sistema consiga reagir às exceções encontradas, inclusive apresentando mensagens de erro aos usuários. Além de auxiliar na resolução do problema, a exibição de mensagens de erro é um fator que contribui bastante para a usabilidade do sistema.

Em JavaScript/TypeScript, há uma classe chamada Error, que nos auxilia com esse processo. Há uma documentação sobre essa classe, que pode consultada aqui.

Etapas envolvidas

Para lidar com essas situações, utilizamos o conceito de exceções, que permite capturar e tratar erros de forma controlada.

  • Lançar uma Exceção: Se um erro é detectado, o programa pode lançar uma exceção usando uma palavra-chave específica (como throw em JavaScript). Isso interrompe o fluxo normal do programa e transfere o controle para um bloco de código que pode tratar a exceção.
    throw new Error("Descreva o erro aqui.");
    
  • Captura e Manipulação: Exceções lançadas são capturadas e tratadas em blocos de código especializados chamados try...catch. Enquanto o primeiro engloba a tentativa de operação que pode envolver erro, o bloco catch contém o código que processa a exceção, podendo registrar informações sobre o erro, tentar uma recuperação ou tomar outras ações apropriadas.
    try {
        // Trecho que pode gerar uma exceção
    } catch (erro: any) {
        // Tratamento da exceção
        console.log(erro);
    }
    

👨🏾‍💻 Exemplo

Vamos considerar a seguinte situação: temos uma classe Pessoa com um atributo chamado nome. De acordo com uma regra da organização, o nome deve ter pelo menos três caracteres.

Para garantir que todas as instâncias da classe Pessoa atendam a essa regra, precisamos validar o nome tanto no construtor quanto no método set. Se o nome fornecido for inválido, lançaremos uma exceção usando a classe Error:

export class Pessoa {
    private _nome: string;

    constructor(nome: string) {
        this.validarNome(nome);
        this._nome = nome;
    }
    
    set nome(nome: string) {
        this.validarNome(nome);
        this._nome = nome;
    }

    get nome(): string {
        return this._nome;
    }

    private validarNome(nome: string): void {
        if (nome.length < 3) {
            throw new Error("O nome precisa ter pelo menos 3 caracteres.");
        }
    }
}

No exemplo acima, se tentarmos criar uma instância da classe Pessoa com um nome inválido (contendo apenas dois caracteres), como em:

let joao: Pessoa = new Pessoa("Jo");  // A exceção será lançada e o programa será interrompido aqui
console.log(joao); // Este código não será executado

A exceção lançada interromperá o fluxo do programa. Para evitar essa interrupção e tratar o erro de forma mais controlada, podemos usar o bloco try...catch:

try { 
    let joao: Pessoa = new Pessoa("Jo");    
    console.log(joao.nome);
}
catch (erro: any) {
    console.log(erro.message);  // Exibe a mensagem da exceção
}

console.log("O programa continuou...");

Neste código, se o nome for inválido, a exceção será capturada pelo bloco catch, e a mensagem de erro será exibida. O programa continuará a execução após o bloco catch, permitindo que o fluxo continue normalmente.

Agora, se o nome for válido, como em:

try { 
    let joao: Pessoa = new Pessoa("Joao");    
    console.log(joao.nome);
}
catch (erro: any) {
    console.log(erro.message);  // Não será executado
}

console.log("O programa continuou...");

Neste caso, a exceção não é lançada, e o programa imprime o nome "Joao" seguido de "O programa continuou...", pois o nome atende ao requisito.

Continuando a execução

No exemplo acima, o objeto joao só existe dentro do escopo do try. Para que esse objeto seja usado posteriormente, precisamos antecipar a sua declaração e, após o try...catch, verificar se ele foi instanciado:

import { Pessoa } from "./Pessoa";

let joao: Pessoa | undefined;

try { 
    joao = new Pessoa("Jo");    
    console.log(joao.nome);
} catch (erro: any) {
    console.log(erro.message);  // Não será executado se não houver erro na criação
}

if (joao instanceof Pessoa) {
    console.log("João existe...");
}

🚀 Para testar na prática

📚 Leituras recomendadas

Há outras informações importantes sobre a classe Error da linguagem JavaScript na documentação elaborada pela Mozilla.