Inicio / Artículos / TypeScript 6 y 7: Inferencia más inteligente, seguida de una reescritura basada en Go.

TypeScript 6 y 7: Inferencia más inteligente, seguida de una reescritura basada en Go.

Aprenda cómo TypeScript 6 corrigió las deficiencias en la inferencia de claves y modernizó los valores predeterminados, sentando las bases para la reescritura total del compilador de TypeScript 7 en Go.

1255 palabras

Recientemente, TypeScript ha mejorado en dos frentes separados, y juntos cuentan una misma historia: el lenguaje se está volviendo tanto más inteligente como más rápido. TypeScript 6, lanzado como la versión final escrita con el compilador tradicional basado en JavaScript antes del gran cambio arquitectónico, se centró en corregir problemas de inferencia que existían desde hacía tiempo y en modernizar los valores predeterminados. Luego, TypeScript 7 dio el paso más radical de reescribir el propio compilador en Go, buscando una velocidad máxima en lugar de nuevas sintaxis. Al analizar ambos conjuntamente se puede ver cómo el conjunto de herramientas llegó a su estado actual: primero volviéndose más preciso y luego mucho más rápido.

Inferencia más inteligente en TypeScript 6

Cualquiera que haya escrito una abreviatura de método y luego visto cómo el parámetro se convertía silenciosamente en any sabe cuán frustrantes pueden ser las antiguas reglas de inferencia de TypeScript. TypeScript 6 aborda ese problema, junto con varios otros vacíos en la inferencia que han llevado a los desarrolladores a buscar respuestas durante años.

Anteriormente, cualquier función que hiciera referencia a this internamente era considerada “sensible al contexto”, lo que significaba que el compilador omitía por completo la inferencia del tipo del parámetro, incluso en casos en los que this nunca se utilizaba realmente dentro del cuerpo de la función:

// Old behavior: 'user' silently became 'any'
const handlers = {
  onSave(user) {
    console.log(user.name); // no autocomplete, no error
  },
};

Con TypeScript 6, el compilador solo considera que una función es sensible al contexto cuando this se utiliza realmente dentro de ella. Si no es así, vuelve a aplicarse la inferencia normal y el parámetro recibe su tipo esperado en lugar de usar any:

// TypeScript 6: 'user' is correctly inferred from context
const handlers: Handlers = {
  onSave(user) {
    console.log(user.name); // fully typed
  },
};

Parece un pequeño ajuste técnico, pero tiene un impacto considerable en el desarrollo diario, especialmente en los proyectos con React y Next.js, donde los métodos de objetos y los manejadores de eventos están por todas partes.

TypeScript 6 también incluye soporte de primera clase para las declaraciones using, que formalizan la gestión explícita de recursos. En lugar de envolver manualmente la lógica de limpieza en un bloque try/finally, se puede dejar que el compilador se encargue automáticamente de su eliminación una vez que un valor sale del ámbito:

function readConfig() {
  using file = openFile("./config.json"); // auto-disposed at scope end
  return JSON.parse(file.read());
}

Las importaciones de subrutas también se vuelven más limpias. El prefijo #/ ahora funciona a través del campo imports, lo que le permite evitar largas cadenas de rutas relativas ../../../:

{
  "imports": {
    "#/*": "./src/*"
  }
}
import { formatCurrency } from "#/utils/currency";
// instead of: import { formatCurrency } from "../../../utils/currency";

Varios valores por defecto también han cambiado. La opción target ahora tiene como valor predeterminado ES2023 en lugar de la antigua base ES3; module tiene como valor predeterminado ESNext, y moduleResolution tiene como valor predeterminado bundler. La opción types también tiene como valor predeterminado un array vacío, lo que impide que TypeScript escanee y cargue automáticamente cada paquete @types que pueda encontrar. Según Microsoft, solo este último cambio ha contribuido a mejoras en el tiempo de compilación del 20 al 50 por ciento, lo que hace que valga la pena revisar su tsconfig.json incluso si no tiene interés en adoptar nuevas características del lenguaje.

En conjunto, las recomendaciones de TypeScript 6 son: auditar cualquier objeto con muchos métodos en su base de código, ya que pueden obtener mejoras automáticas en la seguridad tipológica sin costo alguno; utilizar using cada vez que se traten archivos, conexiones o temporizadores que requieran limpieza; no limitarse a heredar silenciosamente los valores predeterminados del nuevo compilador, sino establecerlos explícitamente en tsconfig.json para que su intención quede clara; y esperar que las definiciones de tipos proporcionadas por frameworks y bibliotecas como React, Redux y las herramientas de Tailwind se actualicen para adaptarse a estos cambios en las semanas siguientes. TypeScript 6 no es una versión provisional: reduce de manera notable la cantidad de sorpresas relacionadas con la inferencia tipológica que se encuentran a diario, lo cual por sí solo es una razón suficiente para actualizar.

La reescritura desde cero en TypeScript 7

Mientras que TypeScript 6 perfeccionó la forma en que el compilador analiza los tipos, TypeScript 7 se enfoca en algo aún más fundamental: la velocidad de ejecución de toda la herramienta. Microsoft reescribió por completo el compilador, el servicio del lenguaje y las herramientas asociadas en Go, reemplazando así la implementación basada en JavaScript que había sustentado a TypeScript durante años. No se trata de un simple aumento de versión; se describe como el cambio de rendimiento más significativo en la historia del lenguaje.

Cualquiera que haya visto el indicador de carga del editor mientras esperaba que apareciera un error de tipo reconocerá exactamente el problema al que se dirige esta reescritura.

Dado que el equipo adaptó la lógica del compilador original en lugar de reconstruir las reglas de verificación de tipos desde cero, la compatibilidad con el código existente se mantuvo en gran medida intacta durante toda la transición.

Los propios resultados de pruebas publicados por Microsoft dan una idea del alcance de las mejoras. En la base de código de VS Code, compuesta por aproximadamente 1,5 millones de líneas, una compilación completa que antes tardaba unos 125 segundos ahora se completa en unos 10 segundos. El tiempo necesario para detectar el primer error de tipo en el editor disminuyó de unos 17 segundos a menos de 1,5 segundos. El uso de memoria disminuyó en aproximadamente un 18 por ciento, y las caídas del servidor de lenguaje disminuyeron en más del 60 por ciento. Tampoco se trata de números puramente sintéticos: empresas como Slack, Figma, Google, Notion y Vercel probaron el nuevo compilador en proyectos reales de producción antes de su lanzamiento y informaron que las mejoras se mantuvieron fuera de los entornos de pruebas controladas.

Para los equipos que trabajan con React y Next.js, esto se traduce en beneficios concretos en el día a día. En un gran monorepo, esperar a que tsc detecte una incompatibilidad de tipos podía tomar varios segundos anteriormente; ese tiempo adicional disminuye drásticamente con el compilador basado en Go:

// Before: waiting on tsc to catch this in a large monorepo could take seconds
interface UserCardProps {
  name: string;
  avatarUrl?: string;
  onSelect: (userId: string) => void;
}

function UserCard({ name, avatarUrl, onSelect }: UserCardProps) {
  // With TS7's native checker, this feedback loop is nearly instant
  return (
    <button onClick={() => onSelect(name)} className="rounded-lg p-2 hover:bg-slate-100">
      {avatarUrl && <img src={avatarUrl} alt={name} className="h-8 w-8 rounded-full" />}
      <span>{name}</span>
    </button>
  );
}

Las aplicaciones grandes de Next.js con cientos de componentes ya no consideran la verificación de tipos como el cuello de botella en los procesos CI que solía ser. Los proyectos construidos con tipos genéricos intensivos, como aquellos que combinan Tailwind y Redux, obtienen compilaciones incrementales notablemente más rápidas. El autocompletado del editor en los grandes monorepos también es mucho más reactivo.

Antes de actualizar, hay algunas consideraciones importantes que cabe señalar. El modo estricto es ahora el predeterminado, por lo que los conjuntos de código anteriormente menos estrictos podrían presentar nuevos errores. Los objetivos de compilación heredados como es5, junto con las configuraciones antiguas de resolución de módulos, ya no son solo advertencias; se tratan como errores graves. Además, dado que la API programática solo es parcialmente estable en esta versión, cualquier framework o herramienta que dependa de ella directamente debería esperar hasta TypeScript 7.1 antes de actualizar.

Nada de esto implica una nueva sintaxis o una gramática fundamentalmente diferente: TypeScript 7 existe principalmente para resolver la queja de décadas sobre el rendimiento del compilador. Si su equipo maneja una gran base de código en React o Next.js, esta es la versión que finalmente hace que tsc parezca algo con lo cual no hay que luchar constantemente. Al igual que con cualquier cambio importante en la infraestructura, vale la pena probarlo primero en una rama secundaria; su pipeline de CI le agradecerá por esa precaución.

Lecturas relacionadas

  • El papel del puente de TypeScript 6 en el camino hacia un compilador nativo de TS 7 — Aprenda cómo TypeScript 6 actualiza las configuraciones predeterminadas, la resolución de módulos y la sintaxis de importación para preparar los repositorios de código para el compilador de TypeScript 7, basado en Go y más rápido.
  • Dentro de la reescritura en Go de TypeScript 7: mejoras de rendimiento sin cambios en el código — Aprenda cómo el compilador basado en Go de TypeScript 7 permite generaciones 8-12 veces más rápidas, por qué funciona el cambio de arquitectura y cómo actualizar de forma segura proyectos existentes.