Los errores de TypeScript pueden parecer crípticos cuando involucran tipos genéricos profundamente anidados, pero con la estrategia correcta se pueden descifrar en segundos. La clave es leer de abajo a arriba, aislar el problema con @ts-expect-error o reveal_type, y usar herramientas que simplifican la representación.
Estrategia de lectura de abajo a arriba
TypeScript muestra los errores con el mensaje más específico al final. El punto de partida siempre es la última línea del error:
// Error típico al usar tipos profundamente anidados:
// Type '{ nombre: string; }' is not assignable to type 'DeepReadonly<Config>'.
// Types of property 'db' are incompatible.
// Type '{ host: string; }' is not assignable to type 'DeepReadonly<{ host: string; puerto: number; }>'.
// Property 'puerto' is missing in type '{ host: string; }' but required
// in type 'DeepReadonly<{ host: string; puerto: number; }>'.
// Lectura: el problema REAL está al final: falta 'puerto' en el objeto 'db'.
// El resto son capas de contexto que ayudan a ubicarlo.
@ts-expect-error para aislar el problema
Cuando un error aparece en medio de una expresión compleja, añadir @ts-expect-error en líneas candidatas ayuda a encontrar dónde empieza realmente:
// Error en cadena: const resultado = pipeline(datos).filtrar(pred).mapear(fn).reducir(acc, 0); // ^^^^^^ error aquí // Aislar paso a paso: const paso1 = pipeline(datos); const paso2 = paso1.filtrar(pred); // @ts-expect-error const paso3 = paso2.mapear(fn); // Si aquí no hay error, el problema está antes // Una vez aislado, leer el tipo de paso2 con reveal_type: // reveal_type(paso2); // Type: Pipeline<string[]> ahí está la pista
reveal_type: inspeccionar tipos en tiempo de compilación
reveal_type es una función incorporada en TypeScript que causa un error con el tipo exacto del argumento. Es perfecta para depurar tipos sin salir del editor:
const usuarios = [
{ id: 1, nombre: 'Ana', activo: true },
{ id: 2, nombre: 'Ben', activo: false },
];
reveal_type(usuarios);
// Type is { id: number; nombre: string; activo: boolean; }[]
const activos = usuarios.filter((u) => u.activo);
reveal_type(activos);
// Type is { id: number; nombre: string; activo: boolean; }[]
// (filter no estrecha el tipo; habría que usar un type predicate)
AssertEqual para comparar tipos exactos
type AssertEqual<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false; type Verificar<T extends true> = T; // Tests de tipo en fichero separado: type T1 = Verificar<AssertEqual<string | number, number | string>>; // true: OK type T2 = Verificar<AssertEqual<ReturnType<typeof JSON.parse>, any>>; // true // type T3 = Verificar<AssertEqual<string, number>>; // Error: false no extiende true
pretty-ts-errors: extensión para VS Code
La extensión pretty-ts-errors para VS Code formatea los errores de TypeScript con colores, sangría y enlaces a la documentación. Es especialmente útil con tipos genéricos anidados porque muestra la jerarquía del error con claridad visual.
Simplify: expandir tipos opacos
// Los tipos que resultan de intersecciones o mapped types suelen mostrarse
// como expresiones, no como objetos planos:
type Simplify<T> = { [K in keyof T]: T[K] } & {};
// Sin Simplify:
type A = { nombre: string } & { edad: number };
// Hover en VS Code: { nombre: string; } & { edad: number; }
// Con Simplify:
type B = Simplify<{ nombre: string } & { edad: number }>;
// Hover: { nombre: string; edad: number } mucho más legible
// Uso práctico en una utility type compleja:
type DeepRequired<T> = Simplify<{
[K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K]
}>;
Tipos circulares
Los errores de tipo circular aparecen cuando un tipo se referencia a sí mismo de forma directa, sin la capa de indirección que TypeScript necesita para diferir la resolución:
// Error: Type alias 'Nodo' circularly references itself
// type Nodo = { valor: number; siguiente: Nodo };
// Solución 1: usar interfaz (las interfaces sí permiten recursividad directa):
interface Nodo { valor: number; siguiente: Nodo | null; }
// Solución 2: añadir indirección con un objeto intermedio:
type Nodo = { valor: number; siguiente: { valor: number } | null };
// Solución 3: usar un genérico deferido:
type Arbol<T> = { valor: T; hijos: Array<Arbol<T>> };
Imagen: Pexels / César Gaviria
