Clean Code en TypeScript: cómo escribir código que no te avergüence mostrar
El código limpio (Clean Code) no es solo un término de moda, sino un enfoque fundamental para el desarrollo que transforma un conjunto caótico de instrucciones en un sistema comprensible y mantenible. Para TypeScript, como lenguaje de tipado estático, los principios del código limpio adquieren una importancia especial: el tipado estricto y las potentes herramientas del compilador permiten detectar errores en la etapa de desarrollo, pero solo si el código está estructurado correctamente. En este artículo, analizaremos los principios clave de Clean Code aplicados a TypeScript, respaldándolos con ejemplos concretos.
1. Nombrado de variables, funciones y clases
El nombre de una variable o función debe responder a la pregunta "¿qué es esto?" o "¿qué hace?". En TypeScript, gracias al tipado, los nombres se vuelven aún más informativos. Evite nombres de una sola letra (excepto índices de bucles) y abreviaciones que solo usted entienda.
// Maloconst d = new Date();const fn = (a: number, b: number) => a + b;
// Buenoconst currentDate = new Date();const sum = (firstNumber: number, secondNumber: number): number => firstNumber + secondNumber;Para las clases, use sustantivos; para las funciones, use verbos o construcciones verbales. Las variables booleanas deben comenzar con is, has, should.
// Maloconst flag = true;function validate() { /* ... */ }
// Buenoconst isUserActive = true;function validateEmail(email: string): boolean { /* ... */ }2. Principio de responsabilidad única (SRP) para funciones
Cada función debe hacer exactamente una cosa y hacerla bien. Si una función realiza varias acciones, divídala en otras más pequeñas. TypeScript permite tipificar estrictamente los datos de entrada y salida, lo que facilita la refactorización.
// Malo: la función busca al usuario, verifica la contraseña y envía un 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'); }}
// Bueno: dividimos la responsabilidadasync 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 en lugar de any
Una de las principales ventajas de TypeScript es su sistema de tipos. Evite any a toda costa. Si el tipo no se conoce de antemano, use unknown, generics o uniones de tipos. Esto hace que el código sea autodocumentado y lo protege de errores.
// Malofunction getData(id: any): any { return fetch(`/api/${id}`);}
// Buenointerface 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 estrictas para describir la estructura de los objetos. Esto simplifica la refactorización y ayuda al IDE a sugerir propiedades.
interface User { readonly id: number; name: string; email: string; role: 'admin' | 'user';}
function updateUserName(user: User, newName: string): User { return { ...user, name: newName };}