TypeScript 7: por qué TypeScript ha tenido que reescribirse en Go
TypeScript lleva más de una década solucionando uno de los grandes problemas del desarrollo con JavaScript: mantener aplicaciones cada vez más grandes sin perder el control sobre el código.
Pero el crecimiento de los proyectos también terminó afectando al propio TypeScript. Repositorios con millones de líneas, árboles de dependencias enormes y cientos de paquetes dentro de un monorepo hicieron que comprobar tipos, compilar y proporcionar información al editor se convirtiera en un problema.
Microsoft ha tomado una decisión poco habitual para solucionarlo: TypeScript 7 ha trasladado su compilador y buena parte de sus herramientas desde TypeScript a Go. No es simplemente una versión con algunos tipos adicionales; es un cambio de arquitectura del propio lenguaje.
El equipo estima mejoras típicas de entre 8 y 12 veces en compilaciones completas. ¿Cómo puede una reimplementación conseguir semejante diferencia y qué cambia para quienes utilizamos TypeScript para desarrollar aplicaciones web?
El curioso caso de un lenguaje escrito en sí mismo
Hasta ahora, el compilador de TypeScript estaba escrito fundamentalmente en TypeScript. Es decir, utilizábamos TypeScript para compilar TypeScript. A este tipo de sistema se le denomina self-hosted.
Cuando ejecutábamos este comando:
Node.js cargaba el compilador de TypeScript y este analizaba nuestro proyecto. El proceso, simplificado, era el siguiente:
Durante años este diseño funcionó extraordinariamente bien y tenía una ventaja evidente: el equipo encargado de crear TypeScript utilizaba su propio lenguaje. Sin embargo, los proyectos modernos han crecido mucho.
El precio de trabajar con millones de líneas de código
Imaginemos un proyecto relativamente grande:
Puede contener cientos de miles de líneas TypeScript; en organizaciones grandes, directamente millones. Al abrirlo, TypeScript necesita comprender archivos, imports, módulos, tipos, interfaces, genéricos, dependencias, declaraciones y referencias entre proyectos.
No basta con analizar cada archivo individualmente: el sistema de tipos necesita conocer las relaciones existentes entre ellos. Por eso una operación aparentemente sencilla puede resultar costosa:
No genera JavaScript, pero sí analiza todo el proyecto para comprobar que los tipos son correctos. En proyectos suficientemente grandes, esa operación se convierte en una parte significativa del tiempo de compilación.
La solución: un compilador nativo
La decisión del equipo de TypeScript fue construir una nueva implementación nativa en Go. El nuevo esquema se parece más a esto:
En lugar de ejecutar el compilador sobre Node.js, tenemos un programa nativo. Esto permite replantear el uso de memoria y aprovechar mejor múltiples núcleos del procesador.
El problema no era simplemente que JavaScript fuese lento
Sería una conclusión demasiado simple decir que Go es más rápido que JavaScript y por eso Microsoft ha reescrito TypeScript. Node.js y V8 son extremadamente rápidos y JavaScript puede ofrecer un rendimiento excelente en muchos tipos de aplicaciones.
El compilador de TypeScript tiene unas características particulares: analiza grandes grafos de archivos y dependencias y realiza enormes cantidades de trabajo sobre estructuras de datos. La implementación histórica tenía además límites importantes para utilizar múltiples núcleos.
Los procesadores actuales pueden tener 8, 12, 16 o 24 núcleos. Sin embargo, disponer de muchos núcleos no sirve de demasiado si una parte importante del trabajo se ejecuta secuencialmente.
Go y el paralelismo
Go resultó atractivo por su modelo de concurrencia. Sus goroutines son tareas ligeras que el runtime puede distribuir entre distintos hilos. Esto permite paralelizar partes del trabajo del compilador.
La realidad interna es más compleja porque los tipos pueden depender unos de otros, pero el objetivo es aprovechar mejor el hardware moderno. TypeScript 7 combina ejecución nativa, memoria compartida y paralelismo para acelerar el análisis de proyectos. El equipo ha indicado que puede realizar en paralelo etapas como el análisis, la comprobación de tipos y la emisión cuando las dependencias lo permiten.
¿Cuánto más rápido es TypeScript 7?
Según las pruebas publicadas por el equipo de TypeScript, las mejoras habituales en compilaciones completas se sitúan aproximadamente entre 8 y 12 veces. No significa que todas las operaciones sean exactamente diez veces más rápidas: el tamaño del repositorio, la configuración, las dependencias y la operación ejecutada afectan al resultado.
Imaginemos únicamente como ejemplo una comprobación de tipos que tarda 20 segundos. Una reducción de diez veces significaría:
Son números ilustrativos, no una promesa de rendimiento. Pero muestran por qué importa la diferencia: cuando una operación deja de interrumpir el flujo de trabajo y se completa casi inmediatamente, cambia la forma de utilizar la herramienta.
El cambio más importante puede estar en el editor
Al hablar de compiladores solemos pensar en el build:
Pero TypeScript también trabaja cuando no estamos compilando. VS Code y otros editores utilizan su servicio de lenguaje para proporcionar autocompletado, navegación, información sobre tipos, detección de errores, refactorizaciones y búsqueda de referencias.
Cuando escribimos user. y aparecen id, name, email y createdAt, hay bastante análisis ocurriendo detrás. En proyectos pequeños apenas se aprecia; en repositorios enormes puede haber retrasos, consumo elevado de memoria o inicializaciones lentas. Por eso acelerar el motor también mejora directamente la experiencia dentro del IDE.
¿Por qué Go?
Microsoft podría haber elegido C++, Rust, C# u otro lenguaje capaz de generar código nativo. Go aporta compilación rápida, soporte multiplataforma, concurrencia integrada, garbage collector, generación sencilla de binarios y un modelo de programación relativamente simple.
También había una condición fundamental: el objetivo no era rediseñar TypeScript, sino trasladar su comportamiento existente a una implementación nativa manteniendo la compatibilidad. Millones de proyectos dependen de que el sistema de tipos continúe comportándose como esperan.
TypeScript 7 no cambia cómo escribimos TypeScript
Un cambio interno tan grande podría hacer pensar que tendremos que aprender un TypeScript diferente. No es el objetivo. Este código sigue siendo TypeScript:
La gran transformación ocurre debajo. Para el desarrollador, el paso es de TypeScript 6 a TypeScript 7; para Microsoft, es el paso de un compilador ejecutado sobre Node.js a un compilador nativo con una arquitectura interna nueva.
¿Qué ocurre con tsc y tsconfig.json?
El objetivo es mantener una experiencia familiar. Seguimos trabajando con tsc y archivos como tsconfig.json:
La idea no es sustituir el ecosistema de TypeScript, sino el motor que hay debajo. Aun así, TypeScript 7.0 no publica todavía una API programática equivalente; las herramientas que la necesitan pueden ejecutarse junto a TypeScript 6 mientras llega la API renovada prevista para una versión posterior.
TypeScript 6 fue una versión de transición
TypeScript 6 no fue simplemente otra versión intermedia. Microsoft la planteó como puente hacia el nuevo compilador: varias opciones antiguas fueron marcadas como obsoletas para preparar los proyectos.
Si mantenemos un proyecto antiguo, actualizar directamente sin revisar la configuración puede no ser una buena estrategia. Conviene comprobar el tsconfig.json y solucionar las advertencias de deprecación antes de completar la migración. El cambio interno busca compatibilidad, pero no implica mantener indefinidamente todas las configuraciones históricas.
Consecuencias para Angular, React y NestJS
TypeScript está profundamente integrado en el ecosistema web. Angular lo utiliza como lenguaje principal, NestJS está diseñado alrededor de TypeScript y una enorme cantidad de proyectos React utilizan .ts y .tsx.
Un proyecto NestJS puede contener controladores, servicios, entidades, DTO, guards, decoradores, módulos y tests con miles de relaciones entre tipos. Una aplicación Angular grande puede incluir cientos de componentes y servicios. Un monorepo puede combinar ambos. Cuanto mayor es el proyecto, más interesante resulta reducir el coste del análisis de tipos.
El impacto en CI/CD
La velocidad del compilador no afecta solamente al ordenador del desarrollador. Pensemos en una pipeline:
Si la comprobación de TypeScript forma parte de cada ejecución, consume tiempo de infraestructura en cada commit, rama y pipeline. Una mejora del compilador puede reducir el tiempo acumulado de CI y, en organizaciones grandes, tener impacto económico además de mejorar la experiencia de desarrollo.
Una lección de arquitectura
La historia de TypeScript 7 ilustra una lección de ingeniería: una arquitectura adecuada hoy no tiene por qué serlo dentro de diez años. El TypeScript original apareció en un contexto muy diferente; crecieron los proyectos, los monorepos, los ordenadores, los IDE y el número de desarrolladores que dependen de él.
Llegó un momento en el que optimizar la arquitectura existente aportaba beneficios limitados. La respuesta fue más radical: cambiar la implementación sin cambiar el lenguaje que ve el usuario. A veces mejorar un sistema significa optimizar una consulta o añadir caché; otras, el problema está en una decisión arquitectónica fundamental que ya no responde a las necesidades actuales.
¿Debemos actualizar todos los proyectos inmediatamente?
No necesariamente. Que TypeScript 7 sea más rápido no significa que debamos actualizar un proyecto de producción sin comprobar antes su compatibilidad. Una migración razonable debería seguir este orden:
- Revisar la versión actual de TypeScript y las opciones obsoletas del
tsconfig. - Ejecutar la comprobación de tipos, los tests y el build de producción.
- Revisar las herramientas que dependen de la API de TypeScript.
- Comparar los tiempos de compilación.
- Actualizar CI/CD después de validar el proyecto.
En proyectos sencillos el cambio puede ser casi transparente. En aquellos con herramientas profundamente integradas en las APIs internas de TypeScript, puede requerir más atención.
Cómo medir si realmente hemos ganado rendimiento
No tiene mucho sentido actualizar únicamente porque Microsoft publique una cifra. Podemos medir nuestro propio proyecto:
Ejecuta varias veces la prueba con la versión anterior y calcula un valor representativo. Después repítela con TypeScript 7. También se puede medir el build completo:
Aquí hay que tener cuidado: el tiempo puede incluir TypeScript, Vite o Webpack, Angular compiler, minificación, procesamiento CSS y generación de assets. Para medir TypeScript hay que intentar aislar TypeScript; de lo contrario, podríamos concluir erróneamente que el nuevo compilador no aporta mejoras cuando el cuello de botella real está en otra parte.
Conclusión
TypeScript nació para resolver un problema de escala. Sus tipos estáticos permitieron mantener grandes aplicaciones JavaScript, pero más de una década después apareció una paradoja: el propio TypeScript empezó a tener problemas para escalar al ritmo de los proyectos que ayudó a crear.
La respuesta de Microsoft fue trasladar el compilador y las herramientas principales a una implementación nativa en Go capaz de aprovechar mejor el hardware moderno. La parte interesante no es simplemente que Go sea más rápido, sino que incluso una tecnología muy exitosa puede llegar a un punto donde continuar optimizando su arquitectura original deja de ser suficiente.
A veces evolucionar significa añadir funcionalidades. Otras veces significa sustituir el motor entero intentando que el usuario apenas note el cambio. TypeScript 7 pertenece claramente al segundo grupo.