Classes tipadas: private, readonly e implements
Uma propriedade pública pode ser alterada de qualquer lugar do código, até para um valor sem sentido. Modificadores de acesso deixam a classe dona das próprias regras.
Estudar esta lição interativa →Uma classe com todas as propriedades públicas (o padrão, se você não escrever nada) deixa qualquer parte do código alterar qualquer coisa, a qualquer momento, inclusive para um valor sem sentido, como um estoque negativo. O modificador private resolve isso: uma propriedade private só pode ser lida ou alterada de dentro da própria classe, nunca de fora.
// private: só a própria classe pode ler ou alterar essa propriedade.
class Estoque {
private quantidade: number;
constructor(inicial: number) {
this.quantidade = inicial;
}
adicionarEstoque(qtd: number) {
this.quantidade += qtd;
}
}
const mouseEstoque = new Estoque(10);
mouseEstoque.adicionarEstoque(5);
console.log("estoque atualizado via método público");
// mouseEstoque.quantidade; // erro: quantidade é privateTente descomentar a linha final e veja o diagnóstico.
Repare que a única forma de mudar o estoque, de fora da classe, é através do método adicionarEstoque, que a própria classe controla (e que poderia, se quisesse, recusar um valor negativo antes de aplicar). É a classe quem decide as regras de alteração dos seus próprios dados, não quem estiver usando ela de fora.
Pense em private como o balcão de uma loja: o cliente pode pedir um produto e receber uma resposta (um método público), mas não pode entrar no estoque e mexer nas caixas diretamente (uma propriedade private). Só quem trabalha ali dentro, os métodos da própria classe, tem acesso direto ao estoque.
Outro modificador comum é readonly: uma propriedade que só pode receber valor na declaração ou dentro do construtor, e depois disso fica travada, mesmo por dentro da classe. A trava é rasa: se a propriedade guarda um objeto ou uma lista, o conteúdo dele ainda pode mudar:
// readonly: só pode ser definida uma vez, no construtor.
class ProdutoCadastro {
readonly id: number;
nome: string;
constructor(id: number, nome: string) {
this.id = id;
this.nome = nome;
}
}
const mouseCadastro = new ProdutoCadastro(1, "Mouse");
mouseCadastro.nome = "Mouse sem fio"; // ok, nome não é readonly
// mouseCadastro.id = 2; // erro: id é readonly, só pode ser definido no construtor
console.log(mouseCadastro);Por fim, uma interface pode servir de contrato para uma classe, com a palavra implements: a classe promete ter todas as propriedades e métodos daquela interface, com os tipos certos, e o TypeScript confere essa promessa assim que você escreve implements.
// implements: a classe promete cumprir o contrato da interface.
interface Pagavel {
pagar(valor: number): string;
}
class CartaoCredito implements Pagavel {
pagar(valor: number): string {
return `R$ ${valor.toFixed(2)} pago no cartão de crédito`;
}
}
const pagamento: Pagavel = new CartaoCredito();
console.log(pagamento.pagar(129.9));Lembra do Módulo 1, apagamento de tipos? private não é exceção: ele é uma checagem que existe só enquanto o TypeScript está checando o seu código, antes de rodar. No JavaScript gerado (o que o navegador realmente executa), essa propriedade vira uma propriedade comum, acessível normalmente. private protege contra erros de quem está escrevendo código TypeScript contra a própria classe, não é uma trava de segurança em tempo de execução. Dá até para contorná-lo de dentro do próprio TypeScript com a notação de colchetes (estoque["quantidade"] passa sem erro). Se você precisa de privacidade real, o JavaScript tem campos privados nativos, escritos com # (como #quantidade), que o TypeScript também entende e que continuam privados depois de compilar.