Quiz: Evaluación de Fundamentos Avanzados

Quiz
10 min~4 min lectura

Quiz Interactivo

Pon a prueba tus conocimientos

Concepto clave

Los tipos genéricos en TypeScript son plantillas de tipos que permiten crear componentes reutilizables y flexibles. Piensa en ellos como moldes para galletas: el molde (genérico) define la forma, pero puedes usar diferentes masas (tipos concretos) para crear galletas variadas. Esto es fundamental en arquitecturas complejas donde necesitas abstraer lógica sin perder seguridad de tipos.

El tipado profundo va más allá de anotaciones superficiales, utilizando tipos condicionales, mapeados y utilitarios para modelar relaciones complejas. Imagina construir un sistema de permisos donde cada usuario tiene roles específicos con accesos detallados; el tipado profundo te permite capturar estas relaciones en tiempo de compilación, previniendo errores que en JavaScript solo aparecerían en runtime.

Cómo funciona en la práctica

Veamos un ejemplo paso a paso de un generic constraint con tipos condicionales:

type ExtractProperty = T[K];

interface User {
  id: number;
  name: string;
  email: string;
}

// Uso:
type UserName = ExtractProperty; // string

Este código define un tipo genérico que extrae una propiedad específica de un objeto. Primero, T representa cualquier tipo, y K está restringido a las claves de T. Luego, T[K] accede al tipo de esa propiedad. En el ejemplo, UserName se infiere como string porque name es una clave de User con tipo string.

Caso de estudio

En una librería de UI para arquitecturas complejas, necesitas un componente DataTable que maneje diferentes fuentes de datos. Usamos genéricos para tipar las columnas y filas dinámicamente:

interface Column {
  key: keyof T;
  label: string;
  render?: (value: T[keyof T]) => React.ReactNode;
}

function DataTable({ data, columns }: { data: T[]; columns: Column[] }) {
  return (
    
          {columns.map(col => (
            
          ))}
        
        {data.map((item, index) => (
          
            {columns.map(col => (
              
            ))}
          
        ))}
      
{col.label}
{col.render ? col.render(item[col.key]) : String(item[col.key])}
); } // Uso con tipo concreto: interface Product { id: number; name: string; price: number; } const products: Product[] = [ { id: 1, name: "Laptop", price: 999 }, { id: 2, name: "Mouse", price: 25 } ]; const columns: Column[] = [ { key: "name", label: "Nombre" }, { key: "price", label: "Precio", render: value => `$${value}` } ]; // DataTable({ data: products, columns });

Este componente acepta cualquier tipo T, asegurando que las columnas se alineen con las propiedades de los datos. El tipado profundo con keyof T previene errores como acceder a propiedades inexistentes.

Errores comunes

  • Usar any en genéricos: Esto anula los beneficios de TypeScript. En lugar de function process(data: T), define restricciones claras como T extends object.
  • Ignorar tipos condicionales: No aprovechar extends para lógica de tipos puede llevar a código redundante. Por ejemplo, en lugar de duplicar funciones para diferentes tipos, usa T extends string ? ... : ....
  • Tipos demasiado amplios: En arquitecturas complejas, genéricos como T sin restricciones pueden causar errores sutiles. Siempre limita con extends para reflejar el dominio real.
  • No usar tipos utilitarios: Reimplementar funcionalidad que ya provee TypeScript, como Partial o Pick, aumenta la complejidad innecesariamente.
  • Descuidar el rendimiento: Tipos recursivos o muy complejos pueden ralentizar el compilador. En grandes proyectos, optimiza con tipos intermedios o type en lugar de interface cuando sea posible.

Checklist de dominio

  • Puedo crear un tipo genérico con múltiples parámetros y restricciones (extends).
  • Utilizo tipos condicionales (T extends U ? X : Y) para modelar lógica avanzada.
  • Aplico tipos mapeados ({ [K in keyof T]: ... }) para transformar estructuras.
  • Integro genéricos en componentes de librerías sin perder inferencia de tipos.
  • Evito any y unknown en contextos genéricos, usando uniones o never en su lugar.
  • Documento genéricos complejos con comentarios que expliquen su propósito en la arquitectura.
  • Pruebo tipos con herramientas como tsd para asegurar comportamientos esperados.

Implementa un sistema de caché tipado con genéricos

En este ejercicio, crearás un sistema de caché genérico para una arquitectura frontend compleja. Sigue estos pasos:

  1. Define una interfaz Cache con métodos get(key: string): T | null, set(key: string, value: T): void, y clear(): void.
  2. Implementa la clase GenericCache que cumpla con la interfaz, usando un Map interno para almacenar pares clave-valor.
  3. Añade un método opcional update(key: string, updater: (current: T) => T): void que actualice un valor existente de forma segura.
  4. Crea un tipo de utilidad CacheConfig que permita configurar claves predefinidas y sus tipos asociados, por ejemplo, para un caché de usuario y producto.
  5. Prueba tu implementación con al menos dos tipos concretos (ej., User y Product) y verifica que TypeScript infiera los tipos correctamente.
Pistas
  • Usa Map para el almacenamiento interno, ya que ofrece mejor rendimiento que objetos planos.
  • Para el tipo CacheConfig, considera usar un tipo mapeado como { [Key in K]: V } para definir las claves.
  • En el método update, asegúrate de verificar si la clave existe antes de aplicar el updater para evitar errores.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.