<h2>O que são Classes Abstratas em TypeScript?</h2>
<p>Uma classe abstrata é um modelo ou contrato que define a estrutura e o comportamento que suas classes filhas devem seguir. Diferentemente de uma classe comum, uma classe abstrata <strong>não pode ser instanciada diretamente</strong>. Seu propósito é servir como uma base para outras classes herdarem seus métodos e propriedades.</p>
<p>Em TypeScript, você declara uma classe abstrata usando a palavra-chave <code>abstract</code>. Isso significa que a classe existe para ser estendida, não para criar objetos dela mesma. É um padrão poderoso para impor consistência em projetos grandes, onde múltiplas classes precisam seguir a mesma interface de implementação.</p>
<h2>Métodos Abstratos: Definindo Contratos</h2>
<h3>O que é um Método Abstrato?</h3>
<p>Um método abstrato é um método declarado dentro de uma classe abstrata <strong>sem implementação</strong>. Ele define apenas a assinatura (nome, parâmetros e tipo de retorno), deixando a implementação real para as classes filhas. Isso garante que toda classe que herdar da abstrata <strong>obrigatoriamente implemente</strong> esse método.</p>
<p>A vantagem principal é que você força o contrato: qualquer desenvolvedor que use sua classe abstrata saberá exatamente quais métodos precisam ser implementados, evitando esquecimentos ou inconsistências.</p>
<h3>Sintaxe Básica</h3>
<pre><code class="language-typescript">abstract class Animal {
abstract fazerSom(): void;
abstract obterEspecie(): string;
}</code></pre>
<p>Aqui, qualquer classe que herdar de <code>Animal</code> <strong>deve</strong> implementar <code>fazerSom()</code> e <code>obterEspecie()</code>. Se não implementar, o TypeScript lançará um erro em tempo de compilação.</p>
<h2>Exemplo Prático: Um Sistema de Pagamentos</h2>
<p>Vou mostrar um caso real que você provavelmente enfrentará na carreira: um sistema que processa diferentes tipos de pagamento (cartão, boleto, transferência bancária).</p>
<pre><code class="language-typescript">abstract class ProcessadorPagamento {
private codigoTransacao: string;
constructor() {
this.codigoTransacao = Math.random().toString(36).substring(7);
}
// Método abstrato - deve ser implementado pelas classes filhas
abstract validarDados(dados: object): boolean;
// Outro método abstrato
abstract processar(valor: number): Promise<boolean>;
// Método concreto - é opcional implementar em subclasses
obterCodigoTransacao(): string {
return this.codigoTransacao;
}
// Método concreto que usa lógica compartilhada
registrarLog(mensagem: string): void {
const timestamp = new Date().toISOString();
console.log([${timestamp}] ${mensagem});
}
}
class PagamentoCartao extends ProcessadorPagamento {
validarDados(dados: { numero: string; cvv: string }): boolean {
return dados.numero.length === 16 && dados.cvv.length === 3;
}
async processar(valor: number): Promise<boolean> {
this.registrarLog(Processando pagamento de R$ ${valor} por cartão);
// Simula validação com banco
return new Promise((resolve) => {
setTimeout(() => {
const sucesso = valor > 0 && valor < 100000;
this.registrarLog(
Cartão: ${sucesso ? 'Aprovado' : 'Recusado'} - TX: ${this.obterCodigoTransacao()}
);
resolve(sucesso);
}, 1000);
});
}
}
class PagamentoBoleto extends ProcessadorPagamento {
validarDados(dados: { codigoBarras: string }): boolean {
return dados.codigoBarras.length === 47;
}
async processar(valor: number): Promise<boolean> {
this.registrarLog(Processando boleto de R$ ${valor});
// Boletos têm processamento mais lento
return new Promise((resolve) => {
setTimeout(() => {
this.registrarLog(
Boleto: Registrado para compensação - TX: ${this.obterCodigoTransacao()}
);
resolve(true);
}, 3000);
});
}
}
class PagamentoTransferencia extends ProcessadorPagamento {
validarDados(dados: { banco: string; conta: string }): boolean {
return dados.banco.length > 0 && dados.conta.length >= 8;
}
async processar(valor: number): Promise<boolean> {
this.registrarLog(Processando transferência de R$ ${valor});
return new Promise((resolve) => {
setTimeout(() => {
this.registrarLog(
Transferência: Iniciada - TX: ${this.obterCodigoTransacao()}
);
resolve(true);
}, 500);
});
}
}
// Usando as classes
async function processarCheckout() {
const pagamentoCartao = new PagamentoCartao();
const dadosCartao = { numero: '1234567890123456', cvv: '123' };
if (pagamentoCartao.validarDados(dadosCartao)) {
const aprovado = await pagamentoCartao.processar(150.00);
console.log(Resultado: ${aprovado ? 'Pagamento realizado' : 'Falha'}\n);
}
const pagamentoBoleto = new PagamentoBoleto();
const dadosBoleto = { codigoBarras: '12345678901234567890123456789012345678901234567' };
if (pagamentoBoleto.validarDados(dadosBoleto)) {
const processado = await pagamentoBoleto.processar(250.00);
console.log(Resultado: ${processado ? 'Boleto registrado' : 'Falha'}\n);
}
}
processarCheckout();</code></pre>
<p>Observe que:</p>
<ul>
<li>As três classes <strong>implementam obrigatoriamente</strong> os métodos abstratos <code>validarDados()</code> e <code>processar()</code></li>
<li>Todas herdam o método <code>registrarLog()</code> sem precisar reimplementá-lo</li>
<li>Você nunca pode fazer <code>new ProcessadorPagamento()</code> — isso vai gerar erro</li>
</ul>
<h2>Quando Usar Classes e Métodos Abstratos</h2>
<h3>Cenários Ideais</h3>
<p>Use classes abstratas quando você tem <strong>uma hierarquia de classes relacionadas</strong> que compartilham comportamento comum, mas têm implementações diferentes. Os padrões mais comuns são:</p>
<ol>
<li><strong>Factory Pattern</strong>: Uma classe abstrata define a interface, subclasses criam implementações específicas</li>
<li><strong>Template Method</strong>: A classe abstrata define o "esqueleto" do algoritmo, subclasses implementam detalhes</li>
<li><strong>Strategy Pattern</strong>: Diferentes estratégias implementam a mesma interface abstrata</li>
</ol>
<h3>O que NÃO Fazer</h3>
<p>Não use classes abstratas para simples herança de propriedades. Se você apenas quer compartilhar dados entre classes, uma classe regular é suficiente. Classes abstratas devem impor um <strong>contrato de implementação</strong>, não apenas reutilizar código.</p>
<pre><code class="language-typescript"></code></pre>
<h2>Propriedades Abstratas: Um Detalhe Importante</h2>
<p>Além de métodos, você também pode definir propriedades abstratas em TypeScript. Isso força as subclasses a declarar essas propriedades:</p>
<pre><code class="language-typescript">abstract class Usuario {
abstract id: number;
abstract nome: string;
abstract email: string;
abstract validarEmail(): boolean;
}
class UsuarioAdmin extends Usuario {
id: number;
nome: string;
email: string;
nivelAcesso: number = 10;
constructor(id: number, nome: string, email: string) {
super();
this.id = id;
this.nome = nome;
this.email = email;
}
validarEmail(): boolean {
return this.email.includes('@empresa.com');
}
}
const admin = new UsuarioAdmin(1, 'João', 'joao@empresa.com');
console.log(Admin validado: ${admin.validarEmail()}); // true</code></pre>
<p>Propriedades abstratas são úteis quando você quer garantir que cada subclasse tenha certas informações obrigatórias, não apenas comportamentos.</p>
<h2>Conclusão</h2>
<p>Aprendemos que <strong>classes abstratas definem contratos</strong> que suas subclasses devem seguir, evitando inconsistências em projetos grandes. Métodos abstratos garantem que comportamentos críticos sejam implementados corretamente em cada variação da classe. A chave é usar abstratas quando você tem múltiplas classes relacionadas que compartilham uma interface comum, mas diferem na implementação — como no exemplo do sistema de pagamentos. Este é um padrão fundamental da programação orientada a objetos e dominar isso coloca você no caminho para escrever código profissional e escalável.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/classes.html#abstract-classes-and-members" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Abstract Classes</a></li>
<li><a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Classes" target="_blank" rel="noopener noreferrer">MDN Web Docs - Herança em JavaScript/TypeScript</a></li>
<li><a href="https://www.oreilly.com/library/view/clean-code-a/9780136083238/" target="_blank" rel="noopener noreferrer">Clean Code by Robert C. Martin - Design Patterns Chapter</a></li>
<li><a href="https://basarat.gitbook.io/typescript/type-system/classes" target="_blank" rel="noopener noreferrer">TypeScript Deep Dive - Abstract Classes</a></li>
<li><a href="https://refactoring.guru/design-patterns" target="_blank" rel="noopener noreferrer">Refactoring.Guru - Design Patterns</a></li>
</ul>
<p><!-- FIM --></p>