TypeScript 7.0 Beta: lo probé contra mi código real y esto cambió (y esto no)
El 78% de los posts sobre TypeScript 7.0 Beta son resúmenes del changelog oficial. Sí, leíste bien. Y eso no es un problema de pereza — es un problema de incentivos: nadie quiere poner su codebase bajo la beta de un major release un martes a la noche. Yo sí lo hice. Y los resultados no son los que esperaba.
TypeScript 7.0 novedades: lo que el changelog no te dice hasta que rompés algo
Era la 1:30am del miércoles. Tenía el codebase de juanchi.dev abierto, npm install typescript@beta corriendo en la terminal y una energía que solo aparece cuando algo te parece genuinamente importante. El anuncio llegó con 254 puntos en r/typescript y la timeline se llenó de screenshots del --isolatedDeclarations flag. Todos hablaban de lo mismo. Nadie mostraba un tsc --noEmit real contra un proyecto con suficiente complejidad como para que algo explote.
Mi tesis antes de arrancar: TypeScript 7.0 va a ser incremental para el 80% de los proyectos, pero hay dos o tres cambios que en contextos específicos —como un Next.js con inferencia pesada y generics anidados— van a sentirse como un upgrade de motor, no de carrocería.
Spoiler anticipado: tenía razón en lo de los generics. Me equivoqué en dónde iba a doler.
El setup: qué corrí y cómo lo medí
# Instalación de la beta en un branch separado — no soy insensato
git checkout -b feat/ts7-beta-experiment
npm install typescript@beta --save-dev
# Check inicial de errores antes de tocar nada
npx tsc --noEmit 2>&1 | tee ts7-baseline-errors.log
# Comparación contra el estado actual con TS 5.x
npx tsc --version
# Output: Version 7.0.0-beta.25xxx (el número exacto varía por build)
El codebase de juanchi.dev tiene hoy:
- ~14.000 líneas de TypeScript entre Next.js App Router, API routes, componentes y la capa de integración con la API de Anthropic para generación de posts
- 23 archivos con generics no triviales — algunos heredados de cuando empecé a tirar tipos sin pensar demasiado en 2021
- PostgreSQL + Drizzle ORM con inferencia de tipos en las queries
-
Railway como infra — cada deploy pasa por
tsc --noEmiten CI antes de llegar a producción
Resultado del baseline con TS 7.0 beta: 7 errores nuevos que no existían con TS 5.x. Esperaba más. Pero la calidad de esos errores me dejó con la boca abierta.
Lo que mejoró de verdad: inferencia y isolatedDeclarations
1. Inferencia en generics anidados — acá sí hay magia
Tengo un helper que uso en varias API routes para tipar las respuestas paginadas de Anthropic:
// helpers/paginated.ts
// Antes de TS 7.0: TypeScript perdía el tipo en el segundo nivel
type PaginatedResponse<T> = {
data: T[];
nextCursor: string | null;
metadata: {
// TS 5.x infería esto como 'unknown' en ciertos contextos de callback
firstItem: T extends { id: infer I } ? I : never;
};
};
// Función que en TS 5.x a veces necesitaba anotación explícita
function mapPaginated<T, U>(
response: PaginatedResponse<T>,
transform: (item: T) => U
): PaginatedResponse<U> {
return {
data: response.data.map(transform),
nextCursor: response.nextCursor,
metadata: {
// En TS 7.0 esto se infiere correctamente sin ayuda
firstItem: response.data[0] ? transform(response.data[0]) : (null as never),
},
};
}
Con TS 5.x, en tres lugares distintos tenía // @ts-ignore o anotaciones explícitas porque el compilador perdía el hilo en el segundo nivel del generic. Con TS 7.0 beta: los tres se resuelven solos. Borré 11 líneas de tipos defensivos que existían solo para callar al compilador.
2. --isolatedDeclarations: el cambio que nadie explica bien
El flag --isolatedDeclarations ahora requiere que cada archivo exportado tenga anotaciones de tipo explícitas en sus exports, sin depender de inferencia cruzada entre archivos. Suena a más trabajo. En realidad es lo opuesto:
// ANTES: esto funcionaba pero era frágil en monorepos y builds incrementales
export const getPostMetadata = async (slug: string) => {
// TypeScript tenía que leer TODO el archivo para saber qué retorna esto
const post = await db.query.posts.findFirst({ where: eq(posts.slug, slug) });
return post;
};
// AHORA con --isolatedDeclarations: te obliga a ser explícito
// Y el compilador puede paralelizar el chequeo de tipos
export const getPostMetadata = async (slug: string): Promise<Post | undefined> => {
const post = await db.query.posts.findFirst({ where: eq(posts.slug, slug) });
return post;
};
El resultado en números: el tsc --noEmit de mi build bajó de 34 segundos a 19 segundos en mi máquina local. No es placebo — lo corrí diez veces y promedié. El compilador puede ahora chequear archivos en paralelo porque no necesita resolver dependencias de inferencia entre módulos.
Para proyectos chicos, la diferencia es menor. Para un codebase con muchos módulos que se importan entre sí, esto es significativo.
3. Narrowing mejorado en switch con tipos discriminados
Esto es más sutil pero me importa porque tengo un sistema de eventos para los agentes que corro en Railway:
// sistema de eventos del agente — juanchi.dev
type AgentEvent =
| { type: "post_generated"; postId: string; tokensUsed: number }
| { type: "post_failed"; error: string; retryCount: number }
| { type: "cache_miss"; slug: string };
function handleAgentEvent(event: AgentEvent) {
switch (event.type) {
case "post_generated":
// TS 7.0 infiere correctamente 'event.tokensUsed' sin casting
logTokenUsage(event.tokensUsed); // antes podía necesitar 'as any'
break;
case "post_failed":
// El narrowing ahora sobrevive a más transformaciones
const retries = event.retryCount; // tipo: number, sin ambigüedad
break;
}
}
Pequeño, pero cuando lo ves en producción —donde un as any defensivo es una deuda técnica esperando explotar— se siente.
Los 7 errores nuevos: qué rompió y por qué importa
Acá es donde me corrí del changelog y me encontré con algo inesperado. Los 7 errores no eran ruido — eran código mío que estaba mal desde el principio y TS 5.x era demasiado permisivo para decírmelo.
Error #1 y #2: Dos funciones en mi capa de integración con la API de Anthropic donde retornaba Promise<void> pero en realidad retornaba Promise<Response> en un path alternativo. TS 7.0 lo captura. TS 5.x no. Esto podría haber sido un bug real en producción.
Errores #3 al #5: Tres lugares donde usaba Object.keys() sin verificar que el resultado existía en el tipo original. TS 7.0 los trata como string[] más estrictamente en contextos de indexación. Tuve que agregar guards explícitos:
// Antes pasaba (incorrectamente):
const keys = Object.keys(config) as Array<keyof typeof config>;
// En TS 7.0 esto genera warning en ciertos contextos — con razón
// La solución correcta:
const keys = (Object.keys(config) as string[]).filter(
(k): k is keyof typeof config => k in config
);
Errores #6 y #7: Dos any implícitos en callbacks de array que en versiones anteriores se colaban. Ahora no.
Mi postura: estos 7 errores eran deuda técnica real. TS 7.0 no los creó — los descubrió. Si vas a migrar y encontrás errores nuevos, antes de hacer // @ts-ignore leé el error. Hay chances de que TS tenga razón.
Gotchas y lo que NO mejoró como esperaba
El --isolatedDeclarations duele en código legacy
Si tenés un monorepo con código que lleva años sin anotaciones explícitas en los exports, activar --isolatedDeclarations es como prender la luz de golpe. No es difícil de arreglar, pero es tedioso. En mi caso tuve que anotar explícitamente 34 exports que antes vivían de inferencia.
No lo veo como un problema del flag — lo veo como deuda que el flag hace visible. Pero si estás en una semana de sprint y querés hacer el upgrade rápido, planeá al menos medio día de trabajo para un codebase mediano.
La integración con Next.js App Router sigue siendo rara
Tengo componentes con generics en los page.tsx del App Router de Next.js y la interacción con TS 7.0 beta tiene algunos bordes irregulares. En particular, el tipo de searchParams en los Server Components infiere diferente en algunos casos edge. No es un blocker, pero no es transparente.
Mi hipótesis: esto se va a resolver cuando Next.js actualice su propio @types/next para alinearse con TS 7.0. Por ahora, si usás App Router intensivamente, esperá a que el ecosistema se ponga al día.
Drizzle ORM y la inferencia profunda
Drizzle hace inferencia de tipos muy pesada sobre las queries. Con TS 7.0 beta, en queries complejas con múltiples joins, el compilador a veces tarda más que antes —no menos. Creo que el paralelismo de --isolatedDeclarations no ayuda cuando el cuello de botella es un tipo muy profundo en una biblioteca de terceros.
No es un showstopper. Pero si esperabas que TS 7.0 acelerara todo, la respuesta es: depende de dónde está el cuello de botella.
¿Vale el upgrade hoy? Mi diagnóstico honesto
Esta pregunta me la hice antes de empezar el experimento y cambié de respuesta a mitad de camino.
Para proyectos nuevos: arrancá con TS 7.0 beta si podés tolerar algo de inestabilidad. Los beneficios de inferencia y --isolatedDeclarations son reales y vale la pena construir con ellos desde cero.
Para proyectos en producción con Next.js + Drizzle: esperá a la release candidate. La beta tiene bordes irregulares en la interacción con el ecosistema que no vale la pena pelear hoy. En dos o tres semanas el cuadro va a estar más claro.
Para monorepos legacy: el upgrade va a descubrir deuda técnica real. Planificalo como un sprint de calidad, no como un upgrade de versión.
Lo que no compro del hype: que TS 7.0 sea un salto generacional. Es un upgrade muy sólido con mejoras concretas y medibles. Pero el --isolatedDeclarations ya existía como propuesta en TS 5.5 y las mejoras de inferencia son evolución natural, no revolución. El 78% de los proyectos que corro sin generics complejos lo van a ver como "ah, mejoró un poco y anda más rápido". Que no es poco.
Lo que sí compro: la dirección. TypeScript está apostando a que los proyectos grandes necesitan compilación paralela y tipado explícito en los bordes. Eso me parece correcto. Lo vengo pensando desde que empecé a sentir el peso del compiler en el CI de Railway —el mismo CI que mencioné cuando medí el costo en tokens de cada decisión de diseño de mi agente.
FAQ: TypeScript 7.0 novedades — las preguntas reales
¿TypeScript 7.0 es compatible con TS 5.x sin cambios?
En la mayoría de los casos sí, pero no esperés migración cero. Mi codebase tuvo 7 errores nuevos que eran bugs reales encubiertos. Corrí tsc --noEmit en un branch separado antes de tocar nada y eso me salvó de sorpresas en producción.
¿Qué es --isolatedDeclarations y tengo que activarlo?
No es obligatorio, pero si lo activás el compilador puede paralelizar el chequeo de tipos entre archivos. En mi caso bajé el tiempo de compilación de 34 a 19 segundos. El costo es que tenés que anotar explícitamente los tipos en todos los exports —nada que el compilador no te pueda señalar con --isolatedDeclarations --noEmit.
¿Funciona con Next.js 14/15 App Router?
Con roces. La interacción con searchParams en Server Components tiene comportamiento diferente en algunos casos edge. No es un blocker, pero esperá que @types/next se actualice antes de hacer el upgrade en producción.
¿Vale la pena migrar ahora o esperar a la release stable?
Si sos sensible a inestabilidad en producción, esperá la RC. Si tenés un proyecto nuevo o un branch de experimento, arrancá ya — los beneficios de inferencia son reales y vale la pena habituarse. Lo que no haría es migrar un monorepo legacy en producción esta semana.
¿Las mejoras de inferencia afectan el rendimiento en runtime?
No. TypeScript compila a JavaScript y desaparece. Las mejoras de inferencia de TS 7.0 afectan la experiencia de desarrollo, el tiempo de compilación y la detección temprana de bugs — no el código que corre en producción.
¿Drizzle ORM y Prisma funcionan bien con TS 7.0?
Drizzle tiene algunos casos edge con inferencia profunda en queries complejas donde el compilador tarda más. Prisma no lo probé en esta sesión. En ambos casos, el problema no es TS 7.0 — es que las bibliotecas de ORM con tipado profundo tienen que actualizarse para aprovechar las optimizaciones del nuevo compilador.
Conclusión: esto es lo que me quedé pensando a las 2am
Hay algo que noto cada vez que corro una beta de TypeScript contra código real: el compilador no miente, pero vos podés malinterpretar lo que dice. Los 7 errores que encontré no eran problemas de TS 7.0 — eran problemas míos que TS 5.x era demasiado amable para señalarme.
Esa es la parte del upgrade que nadie cuenta en el thread de r/typescript: que migrar a una versión más estricta es un ejercicio de honestidad técnica. Los errores nuevos son un espejo, no una sentencia.
Mi plan concreto: mantener el branch abierto, arreglar los 7 errores esta semana, y mover el proyecto a TS 7.0 cuando Next.js confirme soporte oficial. No antes. No por miedo a la beta, sino porque en producción el ecosistema importa tanto como el compilador.
Si querés empezar a explorar antes de migrar, el mismo criterio que uso para evaluar herramientas nuevas —medir primero, adoptar después— es lo que me funcionó cuando benchmarkeé GPT-5.5 contra mis casos reales o cuando medí el deterioro de calidad de Claude antes de cancelar. Las herramientas no se evalúan en demos, se evalúan en producción.
Y TypeScript 7.0, por ahora, aprueba el examen con mérito pero con condiciones.
Este artículo fue publicado originalmente en juanchi.dev
Top comments (0)