TypeScript

Dominando Classes em TypeScript: Modificadores, Readonly e Parameter Properties em Projetos Reais

11 min de leitura

Dominando Classes em TypeScript: Modificadores, Readonly e Parameter Properties em Projetos Reais

Introdução aos Modificadores de Acesso em TypeScript 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: , e , além da palavra-chave que trabalha em conjunto com eles. 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. Public, Private e Protected: Controlando Visibilidade O modificador Public O modificador é o padrão em TypeScript. Qualquer propriedade ou método declarado como (ou sem modificador explícito)

<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(&quot;Ana Silva&quot;, &quot;ana@example.com&quot;);

console.log(usuario.nome); // &quot;Ana Silva&quot; - sem problemas

console.log(usuario.exibirPerfil()); // &quot;Ana Silva (ana@example.com)&quot;</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 &gt; 0) {

this.saldo += valor;

console.log(Depósito de R$ ${valor} realizado.);

}

}

public sacar(valor: number): boolean {

if (valor &gt; 0 &amp;&amp; valor &lt;= 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(&quot;João&quot;, 1000);

conta.depositar(500);

console.log(conta.obterSaldo()); // 1500

// conta.saldo = 99999; // ERRO: Property &#039;saldo&#039; 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(&quot;Rex&quot;);

console.log(dog.status()); // &quot;Rex tem 100 de energia.&quot;

dog.correr();

console.log(dog.status()); // &quot;Rex tem 80 de energia.&quot;

// dog.energia = 50; // ERRO: Property &#039;energia&#039; 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, &quot;Laptop&quot;);

console.log(produto.id); // 1 - leitura permitida

console.log(produto.dataCriacao); // Date object

produto.atualizarNome(&quot;Laptop Dell&quot;); // funciona

// produto.id = 2; // ERRO: Cannot assign to &#039;id&#039; 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 = &quot;1.0.0&quot;;

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(&quot;abc123xyz&quot;);

console.log(config.versao); // &quot;1.0.0&quot;

// config.versao = &quot;2.0.0&quot;; // ERRO: Cannot assign to &#039;versao&#039;</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) =&gt; a + b, 0) / this.notas.length;

}

}

const aluno = new Estudante(

&quot;Carlos&quot;,

&quot;2024001&quot;,

[8, 7.5, 9],

new Date(&quot;2005-03-15&quot;)

);

console.log(aluno.nome); // &quot;Carlos&quot; - public é acessível

aluno.exibirInfo(); // &quot;Carlos - Matrícula: 2024001&quot;

aluno.adicionarNota(8.5);

console.log(aluno.obterMedia()); // 8.25

// aluno.matricula = &quot;2024002&quot;; // 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: &quot;debug&quot; | &quot;info&quot; | &quot;warn&quot; | &quot;error&quot;,

readonly nomeServico: string

) {}

public registrar(nivel: &quot;debug&quot; | &quot;info&quot; | &quot;warn&quot; | &quot;error&quot;, mensagem: string): void {

const niveis = { debug: 0, info: 1, warn: 2, error: 3 };

if (niveis[nivel] &gt;= niveis[this.nivelMinimo]) {

const prefixo = this.timestamp ? [${new Date().toISOString()}] : &quot;&quot;;

const sufixo = [${this.nomeServico}];

console.log(${prefixo} ${nivel.toUpperCase()} ${sufixo}: ${mensagem});

}

}

}

const log = new Logger(true, &quot;info&quot;, &quot;ApiServer&quot;);

log.registrar(&quot;info&quot;, &quot;Servidor iniciado&quot;);

log.registrar(&quot;debug&quot;, &quot;Esta mensagem será ignorada&quot;);

log.registrar(&quot;error&quot;, &quot;Erro crítico detectado&quot;);</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 &quot;isso é interno, não mexe&quot;, &quot;isso pode ler mas não mudar&quot; e &quot;isso é parte da interface pública&quot;. 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>&lt;!-- FIM --&gt;</p>

Comentários

Mais em TypeScript

Boas Práticas de Utility Types em TypeScript: Partial, Required, Pick, Omit e Outros para Times Ágeis
Boas Práticas de Utility Types em TypeScript: Partial, Required, Pick, Omit e Outros para Times Ágeis

Introdução aos Utility Types Os Utility Types são uma funcionalidade poderosa...

O que Todo Dev Deve Saber sobre Template Literal Types em TypeScript: Tipos a partir de Strings
O que Todo Dev Deve Saber sobre Template Literal Types em TypeScript: Tipos a partir de Strings

O que são Template Literal Types? Template Literal Types é um recurso avançad...

Publicando Bibliotecas TypeScript: Types, Exports e Compatibilidade na Prática
Publicando Bibliotecas TypeScript: Types, Exports e Compatibilidade na Prática

Preparando seu Projeto TypeScript para Publicação Antes de publicar uma bibli...