tsgo y tsrs ejecutan cuatro casos sintéticos de TypeScript con la misma revisión de origen; los diagnósticos y códigos de salida coinciden.
Figura: comparación de diagnósticos en cuatro casos sintéticos de un solo archivo. Los cuatro resultados coinciden; no es una prueba de compatibilidad ni de rendimiento de un proyecto.

Cloudflare anunció tsrs con una cifra llamativa: hasta ocho veces más rápido y alrededor de un 70 % menos de memoria que TypeScript 7.0. Sin embargo, al consultar el benchmark fijado a un commit de tsrs vi 16,87× para mui-docs y 13,55× para vscode con las opciones predeterminadas. ¿Cómo encaja eso con los 8× del anuncio?

No repetí el benchmark de rendimiento sobre grandes proyectos. Esta vez hice una prueba más pequeña: ejecuté tsrs y tsgo (la implementación Go de TypeScript) en versiones correspondientes, dentro de un entorno temporal y aislado alojado en GitHub. Comparé cuatro ejemplos TypeScript y los diagnósticos coincidieron en todos ellos. Es una señal para seguir probando, no una razón para reemplazar de inmediato la comprobación de tipos en CI de producción.

Qué mirar en los checkers antes de creer una cifra

El anuncio de Cloudflare resume los resultados iniciales como hasta 8× más rápido y alrededor de un 70 % menos de memoria. En cambio, el informe histórico de tsrs fijado a un commit detalla lo siguiente:

Caso históricotsgotsrscheckersFactor publicado
mui-docs, por defecto12.66 s0.75 s4 vs 1616.87×
mui-docs, ambos --checkers 412.69 s1.07 s4 vs 411.81×
mui-docs, ambos --checkers 1612.36 s0.77 s16 vs 1616.07×
vscode, por defecto11.10 s0.82 s4 vs 1613.55×
vscode, ambos --checkers 166.73 s0.81 s16 vs 168.32×

Las filas por defecto ni siquiera usan el mismo número de checkers: tsgo emplea cuatro y tsrs dieciséis. Pero también superan 8× las filas con cuatro o dieciséis checkers en ambos lados. Son afirmaciones distintas: el anuncio resume una comparación inicial; RESULTS.md es otra instantánea histórica con versiones y condiciones de ejecución concretas. Ocho no es el límite de esta tabla y el factor más alto tampoco se puede prometer en cualquier proyecto.

No puedo deducir una causa única a partir de la tabla. El informe documenta una máquina Depot CI depot-ubuntu-24.04-16 (16 vCPU, 63 GB de RAM, Linux x86_64), con ejecuciones alternadas y medianas para duración y memoria máxima. La base de tsgo es typescript@7.0.2; tsrs porta una revisión posterior de TypeScript 7.1-dev y usa una compilación release optimizada con PGO. Igualar los checkers no iguala las revisiones ni los ajustes de compilación.

La memoria también varía: para mui-docs, el informe indica 4,61× menos con la configuración predeterminada y 8,23× menos cuando ambos usan dieciséis checkers. El «70 % menos» es una síntesis de resultados iniciales, no una garantía por proyecto. Consulté esos datos en el informe original; no realicé el benchmark grande por mi cuenta.

Qué comprobaron realmente los cuatro ejemplos

Fijé tsrs@0.18.0-ts7.1.0-dev.20260929 frente a la implementación Go incluida en typescript@7.1.0-dev.20260930.4. Ambas corresponden al mismo commit de origen b85298b6.

En la preparación cometí un error concreto: traté @typescript/typescript, nombre de un workspace en el repositorio de código fuente, como si fuera un paquete npm público. npm devolvió E404; todavía no había arrancado ningún compilador. El paquete raíz publicado es typescript. En esa nightly el comando sigue siendo tsc y el compilador Go nativo para Linux x64 procede de @typescript/typescript-linux-x64. Al corregir el nombre pude ejecutar la compilación.

Los cuatro ejemplos compartían un tsconfig.json. Dejé types: [] para evitar que el entorno incluyera automáticamente paquetes globales @types y skipLibCheck: false para mantener la comprobación de archivos de declaración. Esta fue la configuración, no solo una referencia vaga a «strict mode»:

{
  "compilerOptions": {
    "strict": true,
    "target": "es2022",
    "module": "esnext",
    "noEmit": true,
    "skipLibCheck": false,
    "types": []
  },
  "include": ["src/*.ts"]
}

En cada directorio hay un único src/test.ts:

Casosrc/test.ts completoResultado tsgo / tsrs
valid (Tipos válidos)export const answer: number = 42;0 diagnósticos, salidas 0 / 0
type-error (Asignación de tipo)export const answer: number = "wrong";TS2322 · salidas 2 / 2
module-missing (Módulo inexistente)import {missing} from "./unavailable.js"; export const answer = missing;TS2307 · salidas 2 / 2
generic-constraint (Restricción genérica)type OnlyText<T extends string> = {value:T}; export type Bad = OnlyText<number>;TS2344 · salidas 2 / 2

Ejecuté ambos con --noEmit --pretty false --checkers 4. Comparé archivo, posición de línea y columna, código de error, mensaje, orden y código de salida; los cuatro casos coincidieron. Como cada ejemplo tiene un único archivo fuente, --checkers 4 no demuestra rendimiento con cuatro procesos de comprobación en paralelo: aquí solo fija un parámetro de la comparación.

Igual cantidad de errores no significa igual diagnóstico

La tabla de compatibilidad de la versión fijada indica que coinciden 13.458 de 13.462 referencias de errores y las 12.779 referencias de .types y .symbols. Pero el recuento total por sí solo no basta.

drizzle-orm es un contraejemplo recogido en el informe original. Con dieciséis checkers a ambos lados, las dos herramientas informaron 10.846 errores, pero en posiciones diferentes, que los autores señalan como un fallo de tsrs. Yo no reproduje ese caso; lo leí en el informe fijado. Para CI o una revisión de código importa que un error apunte al archivo y a la línea correctos.

Hay límites específicos de esta versión: --init y --showConfig aún no funcionan; --locale no traduce los mensajes; los content mappers para .astro, .vue y .svelte funcionan en CLI/build, pero no en el servidor de lenguaje. Los binarios precompilados tampoco incluyen Windows ni macOS Intel. Mi prueba solo cubrió la CLI en Linux aislado de GitHub: no comprobé esos sistemas, los plugins del editor ni el LSP. Si dependes de astro check o de los errores en tiempo real, revisa la tabla de compatibilidad de tu versión. También dejé una nota sobre problemas de compatibilidad entre Astro y TypeScript 7.

Cómo lo probaría en un proyecto propio

Empezaría en un worktree desechable o en otra rama, no en el directorio cotidiano: así es fácil descartar la prueba si no sirve.

El siguiente script evita un fallo frecuente: los dos programas pueden devolver el mismo código de salida y diagnósticos diferentes. set +e es deliberado; los casos con errores deben terminar con código distinto de cero. Primero debemos ejecutar ambos, guardar sus salidas y estados y luego hacer la comparación. Aquí tsc es la CLI Go de la nightly fijada; /tmp/tsgo.out es solo un nombre de archivo de log.

pnpm add -D --save-exact tsrs@0.18.0-ts7.1.0-dev.20260929 typescript@7.1.0-dev.20260930.4

set +e
pnpm exec tsc -p . --noEmit --pretty false --checkers 4 > /tmp/tsgo.out 2>&1
tsgo_status=$?
pnpm exec tsrs -p . --noEmit --pretty false --checkers 4 > /tmp/tsrs.out 2>&1
tsrs_status=$?
diff -u /tmp/tsgo.out /tmp/tsrs.out
diff_status=$?
set -e
printf 'tsgo_exit=%s tsrs_exit=%s diff_exit=%s\n' "$tsgo_status" "$tsrs_status" "$diff_status"
if [ "$tsgo_status" -ne "$tsrs_status" ] || [ "$diff_status" -ne 0 ]; then
  echo 'DIAGNOSTIC_DIFF_OR_EXIT_MISMATCH: inspect /tmp/tsgo.out and /tmp/tsrs.out'
  exit 1
fi

Si la primera comparación difiere, guardaría las salidas sin modificarlas. Después probaría --checkerAssignment go (asignación de archivos e historial de checkers al estilo tsgo) o --noLazyMembers (desactivar la optimización lazy-member de tsrs) en intentos separados. Activarlas antes escondería diferencias del comportamiento predeterminado. Véanse la guía de uso y los valores predeterminados.

El script compara diagnósticos, no tiempos ni memoria. Esta vez no completé las mediciones de rendimiento. Para anotar una ejecución inicial se puede usar GNU time en Linux y recoger duración y pico RSS; en macOS instala gnu-time y cambia la primera línea a TIME_BIN=gtime.

TIME_BIN=/usr/bin/time

set +e
"$TIME_BIN" -f 'elapsed_s=%e max_rss=%M' -o /tmp/tsgo-bench.metrics \
  pnpm exec tsc -p . --noEmit --pretty false --checkers 4 \
  > /tmp/tsgo-bench.out 2>&1
tsgo_status=$?
"$TIME_BIN" -f 'elapsed_s=%e max_rss=%M' -o /tmp/tsrs-bench.metrics \
  pnpm exec tsrs -p . --noEmit --pretty false --checkers 4 \
  > /tmp/tsrs-bench.out 2>&1
tsrs_status=$?
set -e

printf 'tsgo_exit=%s ' "$tsgo_status"; cat /tmp/tsgo-bench.metrics
printf 'tsrs_exit=%s ' "$tsrs_status"; cat /tmp/tsrs-bench.metrics

Una sola pareja de resultados no es un benchmark fiable: la carga de fondo puede cambiarla mucho. Haría al menos cinco rondas emparejadas alternando el orden en el mismo runner, con lockfile, versiones, tsconfig y cantidad de checkers fijos, para comparar medianas y RSS máximo. Separaría arranques fríos de ejecuciones con caché caliente. No vacié la page cache del sistema operativo, así que no lo llamaría una medición con caché de sistema completamente fría. GNU time suele expresar %M en KiB; verifica unidades antes de comparar sistemas.

Antes de sustituir nada, pondría tsrs como trabajo adicional no bloqueante en la CI y mantendría tsc y las comprobaciones del framework como controles obligatorios. Solo si se aclaran las diferencias, hay medidas estables y el sistema supera un ciclo real de PR y despliegue con rollback probado consideraría un cambio. Estos cuatro ejemplos únicamente prueban que ambos compiladores coincidieron en los diagnósticos de esas entradas.

Fecha de la prueba: 22 de octubre de 2026