tutoriales.com

Migración y Tipado de Código Legacy de JavaScript a TypeScript: Guía Paso a Paso

Descubre las mejores estrategias para transformar proyectos complejos de JavaScript en bases de código robustas con TypeScript. Analizamos la configuración gradual, la resolución de tipos implícitos, el uso de JSDoc y patrones para minimizar errores en producción.

Intermedio12 min de lectura6 views
Reportar error

🚀 Introducción a la Migración Progresiva de JavaScript a TypeScript

Migrar una aplicación grande de JavaScript a TypeScript puede parecer una tarea titánica al principio. Muchas organizaciones y desarrolladores se enfrentan al dilema de tener que reescribir todo el código base de golpe, lo cual es arriesgado, costoso y propenso a errores catastróficos en producción. Afortunadamente, TypeScript fue diseñado desde su concepción para permitir una adopción gradual.

En este tutorial exhaustivo, aprenderás cómo transformar un proyecto JavaScript legacy en un sistema fuertemente tipado utilizando estrategias probadas en entornos de producción. Veremos desde la configuración inicial del compilador hasta el manejo de librerías de terceros sin tipos y la refactorización paso a paso.

💡 Consejo: No intentes activar todas las restricciones estrictas del compilador el primer día. El secreto de una migración exitosa es avanzar incrementalmente aplicando tipado blando mediante JSDoc y habilitando opciones estrictas de forma progresiva.

🛠️ Paso 1: Configuración Inicial y Estrategia del Archivo tsconfig.json

El primer paso para iniciar la migración es instalar TypeScript en tu proyecto legacy y configurar el archivo tsconfig.json. La clave para no romper el código existente durante las primeras fases es utilizar banderas flexibles que permitan a JavaScript coexistir pacíficamente con TypeScript.

Ejecuta el siguiente comando para instalar TypeScript en tu proyecto:

npm install --save-dev typescript

A continuación, genera tu archivo de configuración inicial ejecutando:

npx tsc --init

Para una fase inicial de migración, recomendamos un archivo tsconfig.json optimizado para la coexistencia. Modifica tu configuración para incluir las siguientes opciones:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "allowJs": true,
    "checkJs": false,
    "noEmit": true,
    "strict": false,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

Explicación de las banderas clave:

  • allowJs: true: Permite que el compilador procese archivos .js y .jsx, permitiendo que coexistan con los nuevos archivos .ts y .tsx.
  • checkJs: false: Evita que TypeScript reporte errores de tipo masivos en tus archivos JavaScript originales antes de que estés listo para revisarlos.
  • noEmit: true: Útil al principio si tu empaquetador actual (como Webpack o Babel) ya se encarga de compilar el código. TypeScript solo funcionará como validador estático.
  • strict: false: Desactiva temporalmente las reglas estrictas para evitar una avalancha de errores de tipo en funciones heredadas.
1. Configurar allowJs 2. Renombrar .js a .ts 3. Añadir tipos explícitos 4. Activar checkJs y strict mode

📋 Paso 2: Uso de JSDoc como Puente de Transición

Si tu proyecto es masivo y no puedes permitirte renombrar cientos de archivos .js a .ts de inmediato, puedes aprovechar JSDoc para tipar tu código JavaScript actual sin cambiar la extensión de los archivos. El compilador de TypeScript puede leer comentarios JSDoc para inferir y validar tipos.

Considera esta función tradicional en JavaScript:

// utils.js
function calcularTotal(precio, impuesto, descuento) {
    let subtotal = precio + (precio * impuesto);
    return subtotal - descuento;
}

Puedes enriquecerla con JSDoc para que TypeScript comience a validarla:

// utils.js
/**
 * Calcula el total a pagar aplicando impuestos y descuentos.
 * @param {number} precio - El precio base del producto.
 * @param {number} impuesto - El porcentaje de impuesto (ej. 0.16).
 * @param {number} descuento - El valor monetario a descontar.
 * @returns {number} El precio final calculado.
 */
function calcularTotal(precio, impuesto, descuento) {
    let subtotal = precio + (precio * impuesto);
    return subtotal - descuento;
}
📌 Nota: Si en tu tsconfig.json configuras "checkJs": true, TypeScript comenzará a validar automáticamente estos bloques JSDoc en tus archivos JavaScript, ofreciendo autocompletado y detección de errores sin modificar la extensión del archivo.

🔄 Paso 3: Renombrado de Archivos y Resolución de Tipos Implícitos (any implícito)

Una vez que te sientas cómodo con la configuración básica, el siguiente hito en la línea de tiempo de la migración es empezar a renombrar los archivos de .js a .ts (o .tsx si usas React).

Fase A: Renombrar archivos de utilidad pura y funciones auxiliares (sin dependencias complejas del DOM o librerías externas).
Fase B: Renombrar servicios, llamadas a APIs y módulos de lógica de negocio.
Fase C: Renombrar componentes de interfaz de usuario y vistas principales.

Al renombrar tu primer archivo a .ts, es muy probable que te encuentres con el infame error de tipo implícito any:

// usuario.ts
function obtenerNombreCompleto(usuario) {
    // Error: Parameter 'usuario' implicitly has an 'any' type.
    return usuario.nombre + ' ' + usuario.apellido;
}

Solución al tipado de parámetros:

Para solucionar esto, debes crear interfaces o tipos explícitos para tus estructuras de datos:

// usuario.ts
interface UsuarioLegacy {
    nombre: string;
    apellido: string;
    edad?: number;
}

function obtenerNombreCompleto(usuario: UsuarioLegacy): string {
    return `${usuario.nombre} ${usuario.apellido}`;
}
⚠️ Advertencia: Evita la tentación de usar el tipo any para solucionar rápidamente los errores de compilación. El uso indiscriminado de any destruye las garantías de seguridad que ofrece TypeScript. En su lugar, utiliza unknown si el tipo es verdaderamente impredecible, o define interfaces parciales.

📦 Paso 4: Manejo de Librerías de Terceros sin Tipos

Uno de los mayores obstáculos al migrar código legacy es el uso de librerías de JavaScript antiguo que no cuentan con definiciones de tipos oficiales. Cuando importas una librería de este tipo, TypeScript arrojará un error indicando que no encuentra el módulo.

Estrategias para lidiar con librerías externas:

  1. Buscar tipos en DefinitelyTyped: Siempre verifica si existe el paquete @types/nombre-de-libreria en npm.
  2. Crear declaraciones de módulos manuales: Si la librería no tiene tipos, puedes declarar un archivo de ambiente personalizado.

Crea un archivo llamado declarations.d.ts en la raíz de tu carpeta de tipos:

// declarations.d.ts
declare module 'libreria-legacy-custom' {
    export function iniciarPlugin(opciones: Record<string, any>): void;
    export const version: string;
}

Esto le dice a TypeScript que confíe en que la librería existe y expone esas firmas específicas, permitiéndote continuar con la migración sin bloquearte por dependencias externas desactualizadas.


🧗 Paso 5: Incrementando el Nivel de Rigor (Strict Mode)

El paso final y definitivo en la migración de tu proyecto es activar progresivamente las opciones estrictas de TypeScript en el archivo tsconfig.json. No lo hagas todo de golpe; sigue este orden recomendado:

Opción en tsconfigNivel de DificultadDescripción del Impacto
---------
noImplicitAnyFácilObliga a tipar explícitamente cualquier variable o parámetro ambiguo.
strictNullChecksIntermedioImpide que null y undefined se asignen a tipos como string o number por defecto.
---------
strictPropertyInitializationAvanzadoExige que las propiedades de las clases estén inicializadas en el constructor.
strictProActiva todas las banderas estrictas anteriores de manera simultánea.
Progreso de Migración 100%

❓ Preguntas Frecuentes sobre Migración a TypeScript

¿Puedo seguir usando CommonJS y require() durante la migración? Sí, puedes mantener la interoperabilidad configurando allowSyntheticDefaultImports y esModuleInterop en tu tsconfig.json. Sin embargo, se recomienda migrar paulatinamente a la sintaxis de módulos de ES6 (import / export) para aprovechar al máximo el análisis estático de TypeScript.
¿Qué hago si tengo errores en archivos generados automáticamente o en node_modules? Asegúrate de incluir la bandera "skipLibCheck": true en tu configuración. Esto le indica al compilador que omita la verificación de tipos de los archivos de declaración de terceros (d.ts), acelerando drásticamente el tiempo de compilación y evitando errores ajenos a tu código.
¿Es obligatorio compilar a archivos JavaScript separados para producción? No necesariamente. Muchos equipos modernos utilizan herramientas como esbuild, SWC o Babel únicamente para transpilar el código TypeScript a JavaScript eliminando los tipos, mientras confían en tsc --noEmit exclusivamente para la validación estática durante el proceso de integración continua (CI/CD).

🏁 Conclusión

Migrar una aplicación de JavaScript a TypeScript no tiene por qué ser un proceso doloroso ni requerir una reescritura masiva desde cero. Siguiendo un enfoque metódico —comenzando con la configuración flexible de allowJs, utilizando JSDoc como puente, renombrando archivos por fases y endureciendo las reglas del compilador de forma paulatina— transformarás tu código legacy en una aplicación moderna, mantenible y altamente segura.

Recuerda que la meta no es lograr una compilación perfecta el primer día, sino mejorar continuamente la calidad de tu base de código iteración tras iteración.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!