Clean Code em TypeScript: princípios e exemplos na prática
Código limpo (Clean Code) não é apenas um termo da moda, mas uma habilidade fundamental para o desenvolvedor profissional. No contexto do TypeScript, que adiciona tipagem estática ao mundo dinâmico do JavaScript, os princípios do código limpo se tornam ainda mais importantes. O TypeScript nos fornece ferramentas poderosas para autodocumentação do código, mas sem a abordagem correta, elas podem se transformar em caos.
Neste artigo, vamos explorar os principais princípios do Clean Code aplicados ao TypeScript e analisar exemplos práticos que ajudarão você a escrever um código mais legível, sustentável e confiável.
1. Nomenclatura: fale a linguagem do negócio
O primeiro e mais óbvio princípio do Clean Code é a nomenclatura correta. Os nomes de variáveis, funções, classes e interfaces devem refletir sua essência e a intenção do desenvolvedor. O TypeScript, com sua tipagem estrita, permite tornar os nomes ainda mais informativos.
Exemplo ruim:
// O que esta função faz? Quanto tempo leva para entender?function d(a: number, b: number): number { return a * b - (a + b);}
// E o que é esta interface?interface I { n: string; a: number; e: string;}Exemplo bom:
// Função de cálculo de desconto considerando o limitefunction calculateDiscountedPrice(price: number, discountPercent: number): number { const discountAmount = price * (discountPercent / 100); return price - discountAmount;}
// Interface de usuário com campos explícitosinterface UserProfile { name: string; age: number; email: string;}Regras para TypeScript:
- Interfaces — use substantivos sem o prefixo
I(por exemplo,User, nãoIUser). O TypeScript moderno e o ESLint recomendam essa abordagem. - Tipos — para uniões complexas, use a união
|e nomes significativos:StatusType,ApiResponse. - Variáveis booleanas — comece com
is,has,should:isActive,hasPermission. - Funções — use verbos:
getUserData,validateEmail,processOrder.
2. SOLID em TypeScript: aplicação prática
Os princípios SOLID (Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) se encaixam perfeitamente no TypeScript graças ao suporte a interfaces, classes abstratas e genéricos.
Princípio da Responsabilidade Única (SRP): uma classe deve ter apenas um motivo para mudar.
// Violação do SRP: a classe faz demaisclass UserService { createUser(data: any) { /* ... */ } sendEmail(email: string) { /* ... */ } generateReport() { /* ... */ }}
// Abordagem correta: separação de responsabilidadesclass UserRepository { save(user: User): void { /* ... */ }}
class EmailService { sendWelcomeEmail(email: string): void { /* ... */ }}
class ReportGenerator { generateUserReport(users: User[]): Report { /* ... */ }}Princípio da Substituição de Liskov (LSP): as subclasses devem ser intercambiáveis com a classe base. No TypeScript, isso é especialmente relevante ao trabalhar com herança.
// Violação do LSP: a subclass restringe o contratoclass Bird { fly(): void { console.log('Flying'); }}
class Penguin extends Bird { fly(): void { throw new Error('Penguins cannot fly!'); }}
// Correto: extrair interfaceinterface Flyable { fly(): void;}
class Bird implements Flyable { fly(): void { console.log('Flying'); }}
class Penguin { s