um_novo_tipo - VWJavascript/Alurapic GitHub Wiki

Um novo tipo

Uma das grandes vantagem do TypeScript é justamente o que perdemos agora. Será que podemos resolver nosso problema e ainda usar essas vantagens da linguagem? Sim podemos, mas para isso precisaremos criar um novo tipo, isto é, uma nova classe.

No final do arquivo alurapic/client/app/foto/foto.service.ts vamos criar e exportar outra classe chamada MensagemCadastro.

// alurapic/client/app/foto/foto.service.ts 

// código anterior omitido 

// veja que `foto.service.ts` agora exporta duas classes, FotoService e MensagemCadastro!

export class MensagemCadastro {

    mensagem: string;
    inclusao: boolean;

    constructor(mensagem: string, inclusao: boolean) {
        this.mensagem = mensagem;
        this.inclusao = inclusao;
    }
}

Nossa classe possui as propriedades mensagem e inclusao e um construtor. Agora, vamos utilizá-la como retorno do método cadastra de FotoService:

// alurapic/client/app/foto/foto.service.ts 

// código anterior omitido 

cadastra(foto: FotoComponent): Observable<MensagemCadastro> {

        if(foto._id) {
            return this.http.put(this.url + '/' + foto._id, JSON.stringify(foto), 
            { headers: this.headers })
            .map(() => new MensagemCadastro('Foto alterada com sucesso', false));

        } else {
            return this.http.post(this.url, JSON.stringify(foto), 
                { headers: this.headers })
                .map(() => new MensagemCadastro('Foto incluída com sucesso', true));   
        }   
    }

Tanto para a inclusão e alteração passamos para a função map do nosso observable stream uma instância da classe MensagemCadastro que recebe a mensagem específica de cada operação, inclusive indicado se é uma inclusão ou não.

Perfeito! Agora em CadastroComponent, podemos usar o autocomplete porque o TypseScript agora sabe o tipo. Veja que também usamos a mensagem retornada pelo objeto MensagemCadastro.

TypeScript e o favorecimento do encapsulamento

A solução funciona, mas faz sentido podemos alterar a mensagem de MensagemCadastro? Em nosso sistema não faz, mas ninguém impede do programador fazer algo do tipo:

// alurapic/client/app/cadastro/cadastro.component.ts

// código anterior omitido 

    cadastrar(event) {

        event.preventDefault();
        console.log(this.foto);

        this.service
            .cadastra(this.foto)
            .subscribe(res => {

                res.mensagem = 'Esta é minha nova mensagem'; // não deveria permitir adulterar a mensagem que veio do servidor

                this.mensagem = res.mensagem;
                this.foto = new FotoComponent();
                if(!res.inclusao) this.router.navigate(['']);
            }, erro => {
                console.log(erro);
                this.mensagem = 'Não foi possível savar a foto';
            });
    }

Isso acontece porque o modificador de acesso padrão de toda propriedade de uma classe é public, podemos até deixar implícito esse modificador padrão em nosso código se assim desejarmos:

// alurapic/client/app/foto/foto.service.ts 

// código anterior omitido 

export class MensagemCadastro {

    public mensagem: string; // tornando explícito o modificador de acesso public
    public inclusao: boolean;  // tornando explícito o modificador de acesso public

   constructor(mensagem: string, inclusao: boolean) {
        this.mensagem = mensagem;
        this.inclusao = inclusao;
    }
}

O primeiro passo para impedirmos que uma mensagem devolvida por FotoService seja adulterada é escondermos as propriedades mensagem e inclusao de todas as outras classes que não seja a própria classe `FotoService. Conseguimos isso através do modificador de acesso private:

// alurapic/client/app/foto/foto.service.ts 

// código anterior omitido 

export class MensagemCadastro {

    private mensagem: string;
    private inclusao: boolean;

    constructor(mensagem: string, inclusao: boolean) {
        this.mensagem = mensagem;
        this.inclusao = inclusao;
    }
}

Excelente, ninguém mais poderá alterar as propriedades mensagem e inclusao de uma instância de MensagemCadastro, contudo isso nos trouxe um problema. Nosso CadastroComponent precisa consultar as duas propriedades e agora como elas estão encapsuladas em MensagemCadastro o compilador do TypeScript nos dá a seguinte mensagem de erro:

File change detected. Starting incremental compilation...
app/cadastro/cadastro.component.ts(56,37): error TS2341: Property 'mensagem' is private and only accessible within class 'MensagemCadastro'.
app/cadastro/cadastro.component.ts(58,25): error TS2341: Property 'inclusao' is private and only accessible within class 'MensagemCadastro'.

O modificador private

Queremos proibir a escrita nessas propriedades, mas a leitura tem que ser possível, caso contrário todo nosso trabalho foi por água a baixo. Como solucionar? Por padrão, todo método de uma classe também possui modificador public. Que tal criarmos dois métodos públicos que ao serem chamados retornam os valores de mensagem e inclusao respectivamente? Se os métodos fazem parte de MensagemCadastro eles terão acesso às propriedades encapsuladas. Vamos aproveitar e deixar explícito o modificador de acesso public:

// alurapic/client/app/foto/foto.service.ts 

// código anterior omitido 

export class MensagemCadastro {

    private mensagem: string;
    private inclusao: boolean;

    constructor(mensagem: string, inclusao: boolean) {
        this.mensagem = mensagem;
        this.inclusao = inclusao;
    }

    public obterMensagem(): string {
        return this.mensagem;
    }

    public ehInclusao(): boolean {
        return this.inclusao;
    }
}

Agora, precisamos alterar nosso componente CadastroComponent para que faça uso desses métodos no lugar de acessar as propriedades diretamente:

// alurapic/client/app/cadastro/cadastro.service.ts

// código anterior omitido 

cadastrar(event) {

    event.preventDefault();
    console.log(this.foto);

    this.service
        .cadastra(this.foto)
        .subscribe(res => {
            this.mensagem = res.obterMensagem();
            this.foto = new FotoComponent();
            if(!res.ehInclusao()) this.router.navigate(['']);
        }, erro => {
            console.log(erro);
            this.mensagem = 'Não foi possível savar a foto';
        });
}