Saltar al contenido

Lección 20 de 20 · Lectura y práctica

Quiz: Evaluación Final del Proyecto

Tipo de lección: QuizConcepto claveEn arquitecturas frontend complejas, el tipado profundo y los patrones genéricos son fundamentales para crear sistemas escalables y mantenibles. El tipado profundo se refiere a la capacidad de.

Del concepto a la práctica

Taller de esta lección

La evaluación final debe comprobar una cadena completa: entrada validada, transformación tipada, transición permitida y salida observable. El fragmento inicial evidencia el estado ready/invalid por presencia, pero todavía no valida campos ni modela una transición de negocio.

Ejemplo resuelto

ts
type Input = { id: string; title: string };
type State = { kind: "ready"; task: Input } | { kind: "invalid"; message: string };
function prepare(x: Input | null): State { return x ? { kind: "ready", task: x } : { kind: "invalid", message: "faltan datos" }; }
console.log(prepare(null)); // { kind: "invalid", message: "faltan datos" }

Pruébalo

  1. Pasa una tarea válida.

  2. Pasa null.

  3. Lee task solo en ready.

  4. Intenta leerlo en invalid y compila.

Comprueba tu resultado

La entrada válida debe producir kind: "ready"; null debe producir kind: "invalid" con faltan datos.

Error frecuente: evaluar solo que compile. Corrígelo verificando también salidas concretas, no mutación accidental y cobertura de cada variante discriminada.

Comprueba la frontera desconocida

ts
function prepareUnknown(x: unknown): State {
  if (typeof x !== "object" || x === null || !("id" in x) || !("title" in x)
      || typeof x.id !== "string" || !x.id.trim()
      || typeof x.title !== "string" || !x.title.trim()) {
    return { kind: "invalid", message: "faltan datos" };
  }
  return { kind: "ready", task: { id: x.id.trim(), title: x.title.trim() } };
}

Prueba null,{},id3 y espacios como invalid; {id:" t1 ",title:" Leer "} debe producir ready con t1 y Leer. Este guard sí comprueba ejecución; prepare(Input|null) solo confía en el contrato estático. Integra después la transición complete del proyecto y demuestra que una entrada rechazada no modifica el repositorio.

Diagrama de decisión con ramas Sí y No y un bucle que procesa cada elemento de una lista.
CursaloMapa de práctica
Cómo avanza un programa

Una condición decide qué camino ejecuta el programa. Un bucle repite una operación mientras queden elementos, y termina cuando la lista se agota. Separar ambas ideas ayuda a seguir el flujo y comprobar casos límite.

Ver la infografía en tamaño completo ↗
Fuentes para profundizar

Tipo de lección: Quiz

Concepto clave

En arquitecturas frontend complejas, el tipado profundo y los patrones genéricos son fundamentales para crear sistemas escalables y mantenibles. El tipado profundo se refiere a la capacidad de TypeScript para inferir y validar tipos complejos a través de múltiples capas de abstracción, mientras que los patrones genéricos permiten crear componentes reutilizables que se adaptan a diferentes tipos de datos sin perder seguridad en tiempo de compilación.

Imagina que estás construyendo una biblioteca de componentes UI para una empresa grande. Sin tipado profundo, cada componente necesitaría duplicar lógica de validación, y los errores solo aparecerían en tiempo de ejecución. Con TypeScript avanzado, puedes crear un sistema de tipos que valide toda la jerarquía de componentes desde el nivel más bajo hasta las interfaces de usuario, similar a cómo un arquitecto verifica que cada piso de un edificio cumple con las especificaciones estructurales antes de construir el siguiente.

Cómo funciona en la práctica

Veamos un ejemplo paso a paso de cómo implementar un patrón genérico para un sistema de formularios reutilizable:

// Paso 1: Definir tipos base para el formulario
type FormField = {
  value: T;
  validator: (value: T) => boolean;
  errorMessage?: string;
};

// Paso 2: Crear un tipo genérico para el formulario completo
type FormSchema> = {
  [K in keyof T]: FormField;
};

// Paso 3: Implementar una función de validación genérica
function validateForm>(
  form: FormSchema
): boolean {
  return Object.values(form).every(field => field.validator(field.value));
}

// Paso 4: Usar el sistema en un formulario de usuario
const userForm: FormSchema<{
  name: string;
  age: number;
  email: string;
}> = {
  name: {
    value: "Juan Pérez",
    validator: (val) => val.length > 0
  },
  age: {
    value: 30,
    validator: (val) => val >= 18
  },
  email: {
    value: "juan@example.com",
    validator: (val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)
  }
};

const isValid = validateForm(userForm); // TypeScript valida todo el flujo

Caso de estudio

Consideremos una aplicación de e-commerce que necesita manejar diferentes tipos de productos con variaciones específicas. Implementaremos un sistema de tipos que garantice que cada producto tenga las propiedades correctas según su categoría:

Tipo de ProductoPropiedades RequeridasPropiedades Opcionales
Electrónicomodelo, garantíacolor, peso
Ropatalla, materialcolor, temporada
Libroautor, ISBNeditorial, páginas
// Implementación con tipos condicionales
type ProductBase = {
  id: string;
  name: string;
  price: number;
};

type ElectronicProduct = ProductBase & {
  category: "electronic";
  model: string;
  warranty: number;
  color?: string;
  weight?: number;
};

type ClothingProduct = ProductBase & {
  category: "clothing";
  size: string;
  material: string;
  color?: string;
  season?: string;
};

type Product = ElectronicProduct | ClothingProduct;

// Función genérica para procesar productos
function processProduct(
  product: T,
  handler: (p: T) => void
) {
  // TypeScript garantiza que handler recibe el tipo correcto
  handler(product);
}

// Uso type-safe
const laptop: ElectronicProduct = {
  id: "1",
  name: "Laptop",
  price: 999,
  category: "electronic",
  model: "XPS-15",
  warranty: 2
};

processProduct(laptop, (p) => {
  // TypeScript sabe que p es ElectronicProduct
  console.log(`Garantía: ${p.warranty} años`);
});
El poder de TypeScript avanzado está en crear sistemas donde los errores se detectan durante el desarrollo, no en producción. Un tipado bien diseñado actúa como documentación ejecutable que guía a todo el equipo.

Errores comunes

  • Over-engineering de tipos genéricos: Crear tipos demasiado complejos que dificultan la comprensión. Solución: Mantener los genéricos simples y componerlos cuando sea necesario.
  • Ignorar la inferencia de tipos: Especificar tipos manualmente cuando TypeScript puede inferirlos. Solución: Dejar que TypeScript infiera tipos siempre que sea posible y usar anotaciones solo cuando sea necesario.
  • No usar tipos condicionales para uniones: Tratar todas las variantes de un tipo unión de la misma manera. Solución: Usar type guards y tipos condicionales para manejar cada caso específicamente.
  • Descuidar el rendimiento del compilador: Crear tipos recursivos profundos o muy complejos que ralentizan el compilador. Solución: Optimizar tipos complejos y usar técnicas como mapped types solo cuando sean necesarios.
  • No aprovechar las utilidades de tipos: Reimplementar funcionalidad que ya existe en las utilidades de TypeScript. Solución: Familiarizarse con utilidades como Pick, Omit, Partial, y Required.

Checklist de dominio

  1. ¿Puedes crear un sistema de tipos que valide una jerarquía completa de componentes?
  2. ¿Sabes implementar patrones genéricos que se adapten a múltiples casos de uso?
  3. ¿Comprendes cómo usar tipos condicionales para manejar uniones complejas?
  4. ¿Puedes optimizar tipos para mantener buen rendimiento del compilador?
  5. ¿Sabes cuándo usar inferencia de tipos vs anotaciones explícitas?
  6. ¿Puedes crear tipos que actúen como documentación ejecutable?
  7. ¿Entiendes cómo los tipos avanzados mejoran la mantenibilidad a largo plazo?

Implementación de un Sistema de Cache Tipado con Expiración

Objetivo

Crear un sistema de cache genérico con soporte para expiración automática y tipos seguros.

Pasos

  1. Crea una clase genérica TypedCache<T> que almacene valores de tipo T
  2. Implementa métodos para: set(key: string, value: T, ttl?: number), get(key: string): T | undefined, y delete(key: string)
  3. Añade soporte para tiempo de vida (TTL) en milisegundos. Los elementos deben expirar automáticamente
  4. Crea un tipo CacheEntry<T> que incluya el valor, timestamp de creación, y tiempo de expiración
  5. Implementa un método cleanup() que elimine elementos expirados
  6. Agrega tipos para las opciones del cache: CacheOptions con propiedades como defaultTTL
  7. Escribe tests que verifiquen el comportamiento type-safe del cache con diferentes tipos de datos

Requisitos Técnicos

  • Debe funcionar con cualquier tipo de dato (primitivos, objetos, arrays)
  • Los errores de tipo deben detectarse en tiempo de compilación
  • El cleanup debe ejecutarse eficientemente
  • Incluye manejo de casos edge como valores undefined o null
Pistas
  • Considera usar Map en lugar de objeto para almacenar las entradas
  • Puedes usar setTimeout o un interval para el cleanup automático, pero ten en cuenta el rendimiento
  • Para el tipado, explora cómo usar tipos condicionales para manejar valores opcionales

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Sigue con TypeScript Avanzado: Patrones Genéricos y Tipado Profundo para Arquitecturas Complejas

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.