Clean Code em TypeScript: como escrever código que não é vergonha mostrar
Código limpo (Clean Code) não é apenas um termo da moda, mas uma abordagem fundamental para o desenvolvimento que transforma um conjunto caótico de instruções em um sistema compreensível e sustentável. Para TypeScript, como uma linguagem estaticamente tipada, os princípios de código limpo adquirem um significado especial: a tipagem estrita e as ferramentas poderosas do compilador permitem identificar erros durante o desenvolvimento, mas apenas se o código estiver estruturado corretamente. Neste artigo, vamos analisar os princípios-chave do Clean Code aplicados ao TypeScript, apoiando-os com exemplos concretos.
1. Nomenclatura de variáveis, funções e classes
O nome de uma variável ou função deve responder à pergunta "o que é isso?" ou "o que faz?". Em TypeScript, graças à tipagem, os nomes se tornam ainda mais informativos. Evite nomes de uma única letra (exceto para índices de loops) e abreviações que só você entende.
// Ruimconst d = new Date();const fn = (a: number, b: number) => a + b;
// Bomconst currentDate = new Date();const sum = (firstNumber: number, secondNumber: number): number => firstNumber + secondNumber;Para classes, use substantivos; para funções, use verbos ou construções verbais. Variáveis booleanas devem começar com is, has, should.
// Ruimconst flag = true;function validate() { /* ... */ }
// Bomconst isUserActive = true;function validateEmail(email: string): boolean { /* ... */ }2. Princípio da Responsabilidade Única (SRP) para funções
Cada função deve fazer exatamente uma coisa e fazê-la bem. Se uma função executa várias ações — divida-a em partes menores. TypeScript permite tipar estritamente os dados de entrada e saída, o que facilita a refatoração.
// Ruim: a função busca o usuário, verifica a senha e envia um emailasync function loginUser(email: string, password: string): Promise<void> { const user = await findUserByEmail(email); if (user && user.password === password) { await sendLoginEmail(user.email); console.log('Login successful'); }}
// Bom: dividimos a responsabilidadeasync function findUserByEmail(email: string): Promise<User | null> { /* ... */ }function isPasswordValid(inputPassword: string, storedHash: string): boolean { /* ... */ }async function notifyUserLogin(email: string): Promise<void> { /* ... */ }
async function loginUser(email: string, password: string): Promise<boolean> { const user = await findUserByEmail(email); if (!user || !isPasswordValid(password, user.passwordHash)) { return false; } await notifyUserLogin(user.email); return true;}3. Uso de tipos e interfaces em vez de any
Uma das principais vantagens do TypeScript é o sistema de tipos. Evite any a todo custo. Se o tipo não for conhecido antecipadamente, use unknown, generics ou uniões de tipos. Isso torna o código autodocumentado e protege contra erros.
// Ruimfunction getData(id: any): any { return fetch(`/api/${id}`);}
// Bominterface ApiResponse<T> { data: T; error?: string;}
async function getData<T>(id: string): Promise<ApiResponse<T>> { const response = await fetch(`/api/${id}`); return response.json();}Use interfaces estritas para descrever a estrutura dos objetos. Isso simplifica a refatoração e ajuda a IDE a sugerir propriedades.
interface User { readonly id: number; name: string; email: string; role: 'admin' | 'user';}
function updateUserName(user: User, newName: string): User { return { ...user, name: newName };}