Introducción: por qué la refactorización de código legacy no es un lujo, sino una necesidad
\nEn el desarrollo web moderno, TypeScript se ha convertido en el estándar de facto para proyectos grandes. Sin embargo, muchas empresas y startups se enfrentan a un problema: la base de código escrita en JavaScript puro o en versiones tempranas y no estrictas de TypeScript se convierte con el tiempo en un "pantano". La falta de tipos, funciones hinchadas, dependencias implícitas y patrones obsoletos: todo esto ralentiza el desarrollo y aumenta la cantidad de errores.
\nLa refactorización de código legacy es el proceso de mejorar la estructura interna de un programa sin cambiar su comportamiento externo. En el contexto de TypeScript, adquiere un valor especial: no solo limpiamos el código, sino que también obtenemos un potente sistema de tipos que previene errores en la etapa de compilación. En este artículo, desglosaremos un plan paso a paso para la refactorización, desde el análisis inicial hasta las pruebas finales, con ejemplos reales en TypeScript.
\n\n1. Análisis e inventario: ¿por dónde empezar?
\nEl primer paso y el más importante es entender con qué estamos lidiando. No se puede simplemente tomar y reescribir todo de inmediato. Eso es un camino al caos y al tiempo perdido. El enfoque correcto es realizar una auditoría de la base de código.
\n- \n
- Identifica los "puntos calientes". Encuentra los módulos que se modifican con más frecuencia o que contienen más errores. Utiliza herramientas como
git logo SonarQube. \n - Descubre los tipos implícitos. En el código JS legacy, a menudo hay funciones que aceptan
anyo usan@ts-ignore. Haz una lista de esos lugares. \n - Revisa las dependencias. Las bibliotecas obsoletas (por ejemplo,
lodash3.x) pueden dificultar la transición a las API modernas de TypeScript. \n - Escribe pruebas. Antes de cualquier refactorización, cubre las áreas clave del código con pruebas. Esta es tu "red de seguridad". \n
Ejemplo: tienes una función en JS que procesa datos de usuario. En lugar de reescribirla de inmediato, agrega pruebas unitarias con Jest o Vitest:
\n// legacy.js\nfunction processUser(data) {\n return { name: data.name, age: data.age + 1 };\n}\n\n// test.js\nimport { processUser } from './legacy';\ntest('processUser increments age', () => {\n expect(processUser({ name: 'Alice', age: 30 })).toEqual({ name: 'Alice', age: 31 });\n});\n\nSolo después de esto se puede proceder a la refactorización.
\n\n2. Migración gradual al tipado estricto
\nTypeScript ofrece varios niveles de rigor. Para el código legacy, se recomienda comenzar con "strict": false e ir subiendo el nivel gradualmente. Activar todas las opciones de golpe puede romper cientos de archivos.
Pasos clave:
\n- \n
- Activa
noImplicitAny. Esto te obligará a especificar explícitamente los tipos para todos los parámetros y valores de retorno. Al principio puede ser doloroso, pero así es como encontrarás los lugares más peligrosos. \n - Usa
strictNullChecks. Una de las principales causas de errores sonnullyundefined. Esta bandera te obligará a manejarlos explícitamente. \n - Agrega
noUnusedLocalsynoUnusedParameters. Te ayudarán a eliminar el código muerto. \n
Ejemplo de refactorización de una función con tipos implícitos:
\n// Antes de la refactorización (legacy JS)\nfunction calculateTotal(price, tax) {\n return price + (price * tax);\n}\n\n// Después de la refactorización (TypeScript)\ninterface PriceData {\n price: number;\n tax: number;\n}\n\nfunction calculateTotal({ price, tax }: PriceData): number {\n if (price < 0 || tax < 0) {\n throw new Error('Price and tax must be non-negative');\n }\n return price + (price * tax);\n}\n\nObserva: no solo agregamos tipos, sino que también hicimos la API más segura usando una interfaz y validación.
\n\n