Inicio / Artículos / Los éxitos discretos de TypeScript 6 y los hábitos de desarrollo en JavaScript de los desarrolladores senior

Los éxitos discretos de TypeScript 6 y los hábitos de desarrollo en JavaScript de los desarrolladores senior

Descubra las características pasadas por alto de TypeScript 6, como la gestión explícita de recursos y los parámetros de tipo const, además de los modismos en JavaScript en los que confían diariamente los ingenieros senior.

1228 palabras

JavaScript moderno y TypeScript siguen evolucionando de maneras que van mucho más allá de las características destacadas y los puntos del changelog. Actualmente son importantes dos aspectos prácticos: las adiciones más discretas y menos difundidas en TypeScript 6, y los patrones habituales de JavaScript que distinguen las solicitudes de integración de nivel avanzado del simple código funcional. Ambos se refieren en realidad a la misma disciplina: conocer bien las herramientas para elegir la adecuada en lugar de la más familiar. Este artículo analiza ambos aspectos, comenzando por las mejoras de TypeScript 6 que pasaron desapercibidas y luego abordando los patrones de JavaScript a los que recurren por defecto los ingenieros experimentados.

Mejoras discretas de TypeScript 6 que vale la pena adoptar

La mayoría de las coberturas sobre TypeScript 6 se detienen en el hecho de que el modo estricto y ES2025 son ahora los valores predeterminados. Eso sí representa un cambio real, pero eclipsa una serie de adiciones menores que eliminan silenciosamente soluciones temporales utilizadas desde hace tiempo. Si solo lees los titulares de tsconfig-default, te has perdido la parte más útil de esta versión.

Limpieza que no necesitas recordar escribir

Una causa común de errores es olvidarse de cerrar una conexión a la base de datos o eliminar un listener. TypeScript 6 aborda esto mediante una gestión explícita de recursos, permitiéndote marcar un valor para que se elimine automáticamente una vez salga del ámbito de validez.

function readUserSession() {
  using session = openSession(); // auto-disposed at scope end
  const user = session.getUser();
  return user.name;
} // session.dispose() called automatically here

Esto elimina la necesidad de bloques try/finally cuya única función era garantizar la limpieza. La adición sintáctica es sencilla, pero los beneficios en términos de fiabilidad son significativos para cualquier cosa que involucre archivos, sockets o conexiones abiertas.

Tipos literales sin necesidad de usar as const en todas partes

Anteriormente, para mantener intactos los tipos literales a través de una función genérica era necesario dispersar as const por todo el código. TypeScript 6 introduce parámetros de tipo const, que preservan automáticamente la inferencia de los valores literales.

function createConfig<const T extends Record<string, unknown>>(config: T) {
  return config;
}

const config = createConfig({
  env: "production",
  features: ["auth", "billing"],
});
// config.env is "production", not string
// config.features is readonly ["auth", "billing"], not string[]

Si desarrollas clientes de API tipados o escribes creadores de acciones para Redux, esto elimina una gran parte del código genérico as const que de otro modo sería necesario.

Reducción adecuada de tipos dentro de switch (true)

Otra mejora bienvenida: las uniones discriminadas ahora pueden reducirse correctamente dentro de una instrucción switch (true), algo que anteriormente obligaba a utilizar cadenas anidadas de if/else para que el compilador comprendiera los tipos.

function area(shape: Shape): number {
  switch (true) {
    case shape.kind === "circle":
      return Math.PI * shape.radius ** 2; // narrowed to Circle
    case shape.kind === "rectangle":
      return shape.width * shape.height; // narrowed to Rectangle
  }
}

Con este cambio, ya no es necesario abandonar switch solo para mantener intacto el estrechamiento de tipos.

Temporal obtiene tipos de primera clase

TypeScript 6 también incluye tipos integrados para la API Temporal, por lo que ya no hay necesidad de lidiar con Date y sus peculiaridades relacionadas con zonas horarias mediante afirmaciones de tipo manuales.

const meetingStart = Temporal.Instant.from("2026-04-06T10:00:00Z");
const localTime = meetingStart.toZonedDateTimeISO("America/Toronto");

Importaciones de subrutas que realmente se resuelven

También se ha corregido un problema menor pero realmente molesto: las importaciones de subrutas internas ahora se resuelven correctamente, sin necesidad de una larga cadena de segmentos de rutas relativas.

import { User } from "#/models/user.js";
// instead of ../../../models/user.js 🙃

Daniel Rosenwasser, gerente de producto de TypeScript, ha descrito a TS6 como un puente deliberado hacia la próxima versión 7 de TypeScript, basada en Go, lo que indica que no se trata de características experimentales temporales, sino de elementos destinados a permanecer. Si su equipo trabaja con un códigobase completo en JavaScript, React o Next.js, adoptar estas prácticas ahora debería reducir las sorpresas cuando llegue TypeScript 7. En general, TypeScript 6 no se trata solo de nuevos valores predeterminados del compilador; también ofrece un manejo más eficiente de los recursos, una inferencia de tipos más precisa y menos soluciones manuales. Probar los parámetros de tipo using y const en su próxima solicitud de integración podría hacerle preguntarse cómo lograba trabajar sin ellos.

Hábitos de JavaScript que diferencian al código avanzado del resto

Conocer las capacidades más recientes de TypeScript es solo la mitad del cuadro. La otra mitad se refiere a cómo se escribe el propio JavaScript día a día. Cuando la solicitud de cambios de un ingeniero senior está mejor estructurada que las de los demás, rara vez se debe a ingenio; se debe a un conjunto consistente de hábitos aplicados sin vacilación.

Cadenas opcionales y unión con valor nulo

En lugar de cadenas como user && user.profile && user.profile.name, el código de nivel senior utiliza cadenas opcionales combinadas con unión con valor nulo.

const displayName = user?.profile?.name ?? 'Guest';

Es conciso, protege contra valores faltantes y no falla cuando se refactorea el código circundante.

Destructuración con valores por defecto

La destructuración de propiedades, respuestas de API y argumentos de funciones —con valores por defecto razonables integrados— es algo natural en este nivel.

function createUser({ name, role = 'member', isActive = true }) {
  return { name, role, isActive };
}

Hacer esto documenta la forma esperada de los datos directamente en la firma de la función.

Async/Await en lugar de promesas en cadena

El uso de .then().then().catch() sigue funcionando, pero no escala bien a medida que crece la lógica. Compárelo con una versión de async/await:

async function fetchOrders(userId) {
  try {
    const res = await fetch(`/api/orders/${userId}`);
    if (!res.ok) throw new Error('Failed to fetch orders');
    return await res.json();
  } catch (err) {
    console.error('Order fetch failed:', err);
    throw err;
  }
}

El resultado se lee de arriba hacia abajo, lo que facilita su depuración y pruebas.

Métodos de array en lugar de bucles manuales

Utilizar map, filter, reduce y find reemplaza a la mayoría de los bucles for escritos a mano.

const activeAdmins = users
  .filter((u) => u.isActive && u.role === 'admin')
  .map((u) => u.email);

Esto significa menos variables mutables y menos errores por error de desplazamiento en una unidad.

Tipos utilitarios en lugar de interfaces duplicadas

En lugar de redefinir interfaces casi idénticas, el código avanzado en TypeScript utiliza tipos útiles como Partial, Pick y Omit.

type UserFormInput = Pick<User, 'name' | 'email'> & Partial<Pick<User, 'phone'>>;

Eso mantiene una única fuente de verdad, por lo que los cambios en el esquema requieren actualizaciones en menos lugares.

Ganchos y composición en React

Los ganchos personalizados que extraen y encapsulan la lógica aparecen constantemente en bases de código maduras de React.

function useDebouncedValue(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

Un gancho como este permite reutilizar la lógica subyacente y probarla de forma independiente de cualquier componente específico.

Gestión estructurada del estado

Para la gestión del estado, muchos equipos avanzados prefieren las slices de Redux Toolkit a los reducers escritos a mano con instrucciones switch, ya que RTK reduce el código genérico y ofrece inmutabilidad de forma gratuita a través de Immer.

Tal como señaló Douglas Crockford, JavaScript está compuesto por “las partes buenas y las partes malas”: la misma flexibilidad que hace poderosa a este lenguaje también genera confusión. Saber en qué partes apoyarse es lo que distingue a los ingenieros seguros de aquellos que aún están aprendiendo.

Un conjunto breve de principios une todo esto: priorizar la legibilidad sobre la ingeniosidad, dejar que el sistema de tipos de TypeScript se encargue de la documentación en lugar de los comentarios, y construir funcionalidades a partir de funciones pequeñas y componibles en lugar de funciones monolíticas grandes.

Lecturas relacionadas