TypeScript Difícil · Teoría

Comprende antes de memorizar

24 lecciones originales con ejemplos breves, puntos clave y referencias para profundizar.

04

Mapped types avanzados

Los mapped types pueden filtrar claves, cambiar modificadores y transformar valores según la propiedad recorrida.

Ejemplo
type SoloFunciones<T> = { [K in keyof T as T[K] extends Function ? K : never]: T[K] };

Puntos importantes

  • Asignar never en as elimina una clave.
  • La transformación debe conservar una intención de dominio reconocible.
06

Template literal types avanzados

Los patrones con infer pueden analizar cadenas literales y las utilities intrínsecas cambian mayúsculas de forma eficiente.

Ejemplo
type Segmento<T> = T extends `${infer Inicio}/${infer Resto}` ? [Inicio, Resto] : [T, ""];

Puntos importantes

  • El análisis sólo es preciso para literales conocidos.
  • Las expansiones grandes deben generarse antes o simplificarse.
Practicar este concepto
07

Diseño de APIs genéricas

Una API genérica sólida infiere desde argumentos, conserva correlaciones y evita parámetros que sólo aparecen una vez.

Ejemplo
function mapear<T, U>(valores: readonly T[], transformar: (valor: T) => U): U[] { return valores.map(transformar); }

Puntos importantes

  • Coloca parámetros donde el llamador aporta evidencia.
  • No uses constraints amplias que destruyan literales útiles.
Practicar este concepto
08

Varianza

La varianza describe cómo la compatibilidad entre T y U se traslada a productores y consumidores. strictFunctionTypes evita asignaciones inseguras en parámetros de funciones.

Ejemplo
type Productor<out T> = () => T;
type Consumidor<in T> = (valor: T) => void;

Puntos importantes

  • Un productor suele ser covariante y un consumidor contravariante.
  • Las anotaciones in/out validan la varianza estructural; no la fuerzan arbitrariamente.
Practicar este concepto
09

Const type parameters

const ante un parámetro de tipo solicita inferencia literal más específica para argumentos escritos en la llamada.

Ejemplo
function tupla<const T extends readonly unknown[]>(valor: T): T { return valor; }

Puntos importantes

  • Sólo afecta la inferencia, no vuelve inmutable un valor previo.
  • La constraint debe aceptar readonly si esperas tuplas readonly.
Practicar este concepto
10

Branded types

Una intersección con una marca única simula distinciones nominales entre valores estructuralmente iguales; una función de validación debe construir la marca.

Ejemplo
declare const marcaUsuario: unique symbol;
type UsuarioId = string & { readonly [marcaUsuario]: true };

Puntos importantes

  • No exportes assertions que permitan fabricar la marca sin validar.
  • La marca desaparece en runtime.
Practicar este concepto
11

unknown, any y never

unknown acepta cualquier valor pero exige narrowing; any desactiva comprobaciones y se propaga; never no admite valores alcanzables.

Ejemplo
function describir(valor: unknown): string { return typeof valor === "string" ? valor : "otro"; }

Puntos importantes

  • Usa unknown en fronteras no confiables.
  • Reserva any para interoperabilidad acotada y documentada.
Practicar este concepto
12

Excess property checking

Los object literals frescos reciben una comprobación adicional de propiedades desconocidas; una variable intermedia puede ser estructuralmente compatible.

Ejemplo
type Punto = { x: number; y: number };
const origen: Punto = { x: 0, y: 0 };

Puntos importantes

  • No confíes en esta comprobación como validación exacta de objetos.
  • satisfies conserva inferencia y detecta claves sobrantes en literales.
14

Overloads, uniones o genéricos

Usa uniones cuando el retorno no cambia por la entrada, overloads para conjuntos discretos y genéricos cuando debes conservar una relación de tipos.

Ejemplo
function identidad<T>(valor: T): T { return valor; }

Puntos importantes

  • Una unión suele ofrecer mejor narrowing dentro de la implementación.
  • Demasiados overloads vuelven frágil la evolución de la API.
15

Declaration merging

Algunas declaraciones con el mismo nombre se fusionan, especialmente interfaces y namespaces. El orden y la clase de declaración determinan si la mezcla es válida.

Ejemplo
interface Ventana { titulo: string }
interface Ventana { cerrar(): void }

Puntos importantes

  • Los miembros no función deben ser compatibles.
  • La fusión puede extender contratos globales sin crear runtime.
16

Module augmentation

declare module amplía tipos de un módulo existente; el código de runtime que implementa el miembro sigue siendo necesario.

Ejemplo
declare module "./observable" { interface Observable<T> { map<U>(f: (valor: T) => U): Observable<U>; } }

Puntos importantes

  • La resolución usa el mismo identificador que un import.
  • No se pueden introducir exports de nivel superior completamente nuevos mediante augmentation.
Practicar este concepto
17

Archivos .d.ts

Un .d.ts describe tipos públicos sin implementación. Debe reflejar con precisión el JavaScript real y seguir la forma global, módulo o plugin del paquete.

Ejemplo
export declare function analizar(texto: string): { palabras: number };

Puntos importantes

  • declare promete existencia; no crea código.
  • Prueba las declaraciones contra usos válidos e inválidos.
18

Declaraciones ambient

declare introduce valores o módulos proporcionados por otro entorno. Es un contrato de confianza, no una comprobación de disponibilidad.

Ejemplo
declare const VERSION_APLICACION: string;

Puntos importantes

  • Limita globals ambient para evitar colisiones.
  • Prefiere módulos cuando el host permita imports explícitos.
19

Resolución de módulos

moduleResolution modela cómo el host encuentra archivos y paquetes. node16/nodenext respetan exports; bundler asume capacidades de un bundler y no debe elegirse por ensayo.

Ejemplo
{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext" } }

Puntos importantes

  • La resolución de tipos y el emit son decisiones relacionadas pero distintas.
  • Usa traceResolution para investigar, no aliases indiscriminados.
20

Project references

references divide sistemas grandes en proyectos composite con límites y orden de build explícitos.

Ejemplo
{ "files": [], "references": [{ "path": "../core" }, { "path": "../web" }] }

Puntos importantes

  • Cada proyecto referenciado necesita composite.
  • tsc --build coordina estado incremental; el playground no implementa builds multiproyecto.
21

Interoperabilidad con JavaScript

allowJs incorpora JavaScript y checkJs aplica análisis; JSDoc puede expresar contratos gradualmente sin renombrar todos los archivos.

Ejemplo
// @ts-check
/** @param {number} valor */
export function doble(valor) { return valor * 2; }

Puntos importantes

  • checkJs puede activarse por proyecto o archivo.
  • Las declaraciones externas siguen siendo necesarias para dependencias sin tipos.
22

Decorators modernos

TypeScript 5.9 soporta la propuesta moderna de decorators con funciones de contexto sin activar experimentalDecorators. TC39 todavía no la ha convertido en estándar final; no incluye parameter decorators ni emitDecoratorMetadata del modelo legacy.

Ejemplo
function registrado<T extends new (...args: any[]) => object>(clase: T, contexto: ClassDecoratorContext) { contexto.addInitializer(() => console.log(clase.name)); }
@registrado class Servicio {}

Puntos importantes

  • El contexto depende del elemento decorado y permite addInitializer.
  • No mezcles firmas modernas con decorators legacy experimentales.
Practicar este concepto
23

Pruebas de tipos

Las APIs de tipos necesitan casos que deban compilar y casos marcados con @ts-expect-error. Las igualdades condicionales permiten fijar inferencias sin ejecutar código.

Ejemplo
type Igual<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;

Puntos importantes

  • @ts-expect-error falla si el error esperado desaparece.
  • Prueba comportamiento público, no detalles accidentales del checker.
Practicar este concepto
24

Mantenibilidad y rendimiento de tipos

Tipos públicos legibles, alias intermedios y distribuciones acotadas reducen instanciaciones y mejoran diagnósticos. La máxima precisión no siempre compensa el coste cognitivo o de compilación.

Ejemplo
type Identificador<T extends string> = `${T}:${string}`;

Puntos importantes

  • Mide con extendedDiagnostics y generateTrace en proyectos reales.
  • Evita uniones cartesianas y recursión profunda en APIs públicas.