<h2>Introdução aos Modificadores de Acesso em TypeScript</h2>
<p>Os modificadores de acesso são um dos pilares da Programação Orientada a Objetos e determinam a visibilidade de propriedades e métodos dentro de uma classe. Em TypeScript, você tem controle total sobre o que fica privado, protegido ou público, permitindo criar abstrações robustas e interfaces seguras. Diferentemente de JavaScript puro, TypeScript oferece três modificadores principais: <code>public</code>, <code>private</code> e <code>protected</code>, além da palavra-chave <code>readonly</code> que trabalha em conjunto com eles.</p>
<p>Compreender quando usar cada modificador é fundamental para escrever código mantível e seguro. Um modificador bem escolhido evita que dados sensíveis sejam acessados diretamente, força o uso de métodos controlados e deixa clara a intenção do código para quem o lê. Vou guiá-lo através de cada um deles com exemplos práticos que você pode usar no seu dia a dia.</p>
<h2>Public, Private e Protected: Controlando Visibilidade</h2>
<h3>O modificador Public</h3>
<p>O modificador <code>public</code> é o padrão em TypeScript. Qualquer propriedade ou método declarado como <code>public</code> (ou sem modificador explícito) pode ser acessado de qualquer lugar: dentro da classe, fora dela, em subclasses e em qualquer código cliente. Use <code>public</code> quando o membro faz parte da interface pública da sua classe.</p>
<pre><code class="language-typescript">class Usuario {
public nome: string;
public email: string;
constructor(nome: string, email: string) {
this.nome = nome;
this.email = email;
}
public exibirPerfil(): string {
return ${this.nome} (${this.email});
}
}
const usuario = new Usuario("Ana Silva", "ana@example.com");
console.log(usuario.nome); // "Ana Silva" - sem problemas
console.log(usuario.exibirPerfil()); // "Ana Silva (ana@example.com)"</code></pre>
<h3>O modificador Private</h3>
<p>O modificador <code>private</code> restringe o acesso a um membro apenas dentro da classe onde foi declarado. Nenhuma subclasse pode acessar membros privados, e código externo definitivamente não pode. Use <code>private</code> para dados internos que não devem ser expostos diretamente, como credenciais ou estado sensível.</p>
<pre><code class="language-typescript">class ContaBancaria {
public titular: string;
private saldo: number; // Apenas leitura e escrita dentro da classe
constructor(titular: string, saldoInicial: number) {
this.titular = titular;
this.saldo = saldoInicial;
}
public depositar(valor: number): void {
if (valor > 0) {
this.saldo += valor;
console.log(Depósito de R$ ${valor} realizado.);
}
}
public sacar(valor: number): boolean {
if (valor > 0 && valor <= this.saldo) {
this.saldo -= valor;
console.log(Saque de R$ ${valor} realizado.);
return true;
}
return false;
}
public obterSaldo(): number {
return this.saldo; // Acesso controlado via método
}
}
const conta = new ContaBancaria("João", 1000);
conta.depositar(500);
console.log(conta.obterSaldo()); // 1500
// conta.saldo = 99999; // ERRO: Property 'saldo' is private</code></pre>
<h3>O modificador Protected</h3>
<p>O modificador <code>protected</code> é um meio termo entre <code>public</code> e <code>private</code>. Membros protegidos podem ser acessados dentro da classe e também dentro de subclasses, mas não fora delas. Use <code>protected</code> quando quiser criar uma hierarquia de classes e compartilhar dados entre classe base e filhas.</p>
<pre><code class="language-typescript">class Animal {
public nome: string;
protected energia: number; // Acessível em subclasses
constructor(nome: string) {
this.nome = nome;
this.energia = 100;
}
protected gastarEnergia(quantidade: number): void {
this.energia = Math.max(0, this.energia - quantidade);
}
public status(): string {
return ${this.nome} tem ${this.energia} de energia.;
}
}
class Cachorro extends Animal {
public correr(): void {
console.log(${this.nome} está correndo!);
this.gastarEnergia(20); // Pode acessar protected
}
}
const dog = new Cachorro("Rex");
console.log(dog.status()); // "Rex tem 100 de energia."
dog.correr();
console.log(dog.status()); // "Rex tem 80 de energia."
// dog.energia = 50; // ERRO: Property 'energia' is protected</code></pre>
<h2>Readonly: Imutabilidade Garantida</h2>
<h3>Entendendo a Imutabilidade com Readonly</h3>
<p>O modificador <code>readonly</code> (que pode ser combinado com os anteriores) faz com que uma propriedade não possa ser modificada após sua inicialização. Diferentemente de <code>private</code>, uma propriedade <code>readonly</code> pode ser <code>public</code> e ser lida por qualquer um, mas sua escrita é bloqueada. Isso é extremamente útil para valores que não devem mudar, como identificadores ou configurações.</p>
<p>A grande vantagem do <code>readonly</code> é a segurança em tempo de compilação. O TypeScript impedirá que você tente reatribuir o valor, evitando bugs sutis que passariam despercebidos em JavaScript puro.</p>
<pre><code class="language-typescript">class Produto {
readonly id: number;
public nome: string;
readonly dataCriacao: Date;
constructor(id: number, nome: string) {
this.id = id;
this.nome = nome;
this.dataCriacao = new Date();
}
public atualizarNome(novoNome: string): void {
this.nome = novoNome;
}
}
const produto = new Produto(1, "Laptop");
console.log(produto.id); // 1 - leitura permitida
console.log(produto.dataCriacao); // Date object
produto.atualizarNome("Laptop Dell"); // funciona
// produto.id = 2; // ERRO: Cannot assign to 'id' because it is a read-only property</code></pre>
<h3>Readonly com Modificadores de Acesso</h3>
<p>Você pode combinar <code>readonly</code> com <code>public</code>, <code>private</code> ou <code>protected</code> para criar diferentes níveis de restrição. Uma propriedade <code>private readonly</code> é um membro totalmente imutável e inacessível, enquanto <code>public readonly</code> permite leitura mas impede modificação externa.</p>
<pre><code class="language-typescript">class Configuracao {
public readonly versao: string = "1.0.0";
private readonly chaveSecreta: string;
protected readonly timeout: number;
constructor(chaveSecreta: string) {
this.chaveSecreta = chaveSecreta; // Inicialização é permitida
this.timeout = 30000;
}
public obterVersao(): string {
return this.versao; // Leitura sem problemas
}
private obterChave(): string {
return this.chaveSecreta; // Acessível apenas internamente
}
}
const config = new Configuracao("abc123xyz");
console.log(config.versao); // "1.0.0"
// config.versao = "2.0.0"; // ERRO: Cannot assign to 'versao'</code></pre>
<h2>Parameter Properties: Sintaxe Reduzida</h2>
<h3>O que São Parameter Properties</h3>
<p>Parameter Properties (ou propriedades de parâmetro) são um açúcar sintático do TypeScript que permite declarar, inicializar e atribuir modificadores a propriedades de classe diretamente na lista de parâmetros do construtor. Em vez de declarar a propriedade, depois declará-la novamente no construtor e atribuir o parâmetro, você faz tudo em uma linha. Isso reduz boilerplate significativamente.</p>
<pre><code class="language-typescript">// Forma tradicional (verbose)
class Pessoa {
private nome: string;
private idade: number;
constructor(nome: string, idade: number) {
this.nome = nome;
this.idade = idade;
}
}
// Com Parameter Properties (concisa)
class Pessoa {
constructor(private nome: string, private idade: number) {}
}</code></pre>
<h3>Sintaxe e Exemplos Práticos</h3>
<p>A sintaxe é simples: coloque o modificador de acesso (e opcionalmente <code>readonly</code>) antes do parâmetro no construtor. TypeScript automaticamente criará a propriedade com aquele modificador e atribuirá o valor do parâmetro a ela. Isso funciona com <code>public</code>, <code>private</code>, <code>protected</code> e <code>readonly</code>.</p>
<pre><code class="language-typescript">class Estudante {
constructor(
public nome: string,
private matricula: string,
protected notas: number[],
readonly dataNascimento: Date
) {}
public exibirInfo(): void {
console.log(${this.nome} - Matrícula: ${this.matricula});
}
public adicionarNota(nota: number): void {
this.notas.push(nota);
}
public obterMedia(): number {
return this.notas.reduce((a, b) => a + b, 0) / this.notas.length;
}
}
const aluno = new Estudante(
"Carlos",
"2024001",
[8, 7.5, 9],
new Date("2005-03-15")
);
console.log(aluno.nome); // "Carlos" - public é acessível
aluno.exibirInfo(); // "Carlos - Matrícula: 2024001"
aluno.adicionarNota(8.5);
console.log(aluno.obterMedia()); // 8.25
// aluno.matricula = "2024002"; // ERRO: private
// aluno.dataNascimento = new Date(); // ERRO: readonly</code></pre>
<h3>Combinando Parameter Properties com Métodos</h3>
<p>Parameter Properties brilham quando você precisa criar classes simples com comportamento. Compare a clareza: uma classe que poderia ter dez linhas fica com três. O código fica mais legível porque você vê imediatamente quais são as dependências da classe e seus modificadores.</p>
<pre><code class="language-typescript">class Logger {
constructor(
private timestamp: boolean,
private nivelMinimo: "debug" | "info" | "warn" | "error",
readonly nomeServico: string
) {}
public registrar(nivel: "debug" | "info" | "warn" | "error", mensagem: string): void {
const niveis = { debug: 0, info: 1, warn: 2, error: 3 };
if (niveis[nivel] >= niveis[this.nivelMinimo]) {
const prefixo = this.timestamp ? [${new Date().toISOString()}] : "";
const sufixo = [${this.nomeServico}];
console.log(${prefixo} ${nivel.toUpperCase()} ${sufixo}: ${mensagem});
}
}
}
const log = new Logger(true, "info", "ApiServer");
log.registrar("info", "Servidor iniciado");
log.registrar("debug", "Esta mensagem será ignorada");
log.registrar("error", "Erro crítico detectado");</code></pre>
<h2>Conclusão</h2>
<p>Você aprendeu três conceitos fundamentais que transformam a qualidade do seu código TypeScript. Primeiro, os modificadores <code>public</code>, <code>private</code> e <code>protected</code> permitem controlar a visibilidade e criar abstrações seguras — use <code>private</code> para ocultar detalhes de implementação, <code>protected</code> para compartilhar entre hierarquias e <code>public</code> apenas para o que deve ser exposto. Segundo, <code>readonly</code> oferece imutabilidade em tempo de compilação, prevenindo reatribuições acidentais de valores que não devem mudar, e combina-se perfeitamente com os outros modificadores. Terceiro, Parameter Properties eliminam boilerplate repetitivo, deixando seu construtor limpo e declarativo — declare tudo em uma linha em vez de três.</p>
<p>A verdadeira maestria vem de saber quando usar cada um. Uma classe bem estruturada com modificadores apropriados é um contrato claro entre você e quem usa seu código: ela diz explicitamente "isso é interno, não mexe", "isso pode ler mas não mudar" e "isso é parte da interface pública". Comece aplicando esses conceitos em seus projetos e você verá como bugs diminuem e a manutenção fica mais tranquila.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/classes.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Classes</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/classes.html#member-visibility" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Member Visibility</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes" target="_blank" rel="noopener noreferrer">MDN Web Docs - Classes (JavaScript/TypeScript context)</a></li>
<li><a href="https://effectivetypescript.com/" target="_blank" rel="noopener noreferrer">Effective TypeScript - Dan Vanderkam</a></li>
<li><a href="https://basarat.gitbook.io/typescript/" target="_blank" rel="noopener noreferrer">TypeScript Deep Dive - Basarat Ali Syed</a></li>
</ul>
<p><!-- FIM --></p>