Saltar al contenido

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

Quiz: Patrones de Diseño en TypeScript

Tipo de lección: QuizConcepto claveLos patrones de diseño en TypeScript son soluciones reutilizables a problemas comunes en el desarrollo de software, pero con un enfoque especial en el sistema de tipos. A nivel avanzado, no se.

Del concepto a la práctica

Taller de esta lección

La elección del patrón depende del problema: Factory centraliza creación, Builder ordena pasos y una unión discriminada limita variantes. Este caso permite compararlos sin añadir clases innecesarias.

Ejemplo resuelto

ts
type Parser = { format: "csv"; parse(s: string): string[] } | { format: "line"; parse(s: string): string[] };
const parser: Parser = { format: "csv", parse: s => s.split(",") };
console.log(parser.parse("a,b")); // ["a", "b"]

Pruébalo

  1. Decide si una nueva instancia necesita Factory o solo un literal.

  2. Implementa el formato line con split("|").

  3. Procesa "x|y".

  4. Rechaza una variante sin parse.

Comprueba tu resultado

El separador de barras debe devolver ["x","y"]; el de comas devuelve ["a","b"].

Error frecuente: crear una jerarquía de clases para dos comportamientos simples. Corrígelo manteniendo una unión y funciones pequeñas; usa una Factory solo si la selección o construcción se repite en varios consumidores.

Este split por comas es un ejemplo de separación simple, no un parser CSV completo: no admite comas entre comillas, escapes ni saltos dentro de un campo. El formato llamado line en el ejercicio usa barras; documenta ese contrato aunque su nombre sea simplificado. Una unión con ambas firmas iguales conserva la etiqueta, pero no demuestra un Builder ni una jerarquía. Justifica la Factory cuando centralizas selección repetida, sin añadirla por obligación.

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

Los patrones de diseño en TypeScript son soluciones reutilizables a problemas comunes en el desarrollo de software, pero con un enfoque especial en el sistema de tipos. A nivel avanzado, no se trata solo de implementar patrones clásicos como Singleton o Factory, sino de aprovechar el tipado genérico para hacerlos más seguros, flexibles y adaptables a arquitecturas complejas.

Imagina que estás construyendo una biblioteca de componentes frontend. Sin patrones de diseño, cada componente podría tener su propia lógica ad-hoc, lo que lleva a código duplicado y difícil de mantener. Con patrones bien aplicados y tipados, puedes crear una arquitectura coherente donde los tipos guían el uso correcto y previenen errores en tiempo de compilación. Por ejemplo, el patrón Strategy con genéricos permite definir algoritmos intercambiables con tipos específicos para cada contexto, asegurando que solo se usen las estrategias válidas.

Cómo funciona en la práctica

Vamos a implementar el patrón Observer con TypeScript avanzado, usando genéricos para tipar los eventos y sus datos. Este patrón es clave en arquitecturas reactivas, como en sistemas de estado o notificaciones.

// Definición de tipos genéricos para Observer
interface EventData {
    type: string;
    payload: any;
}

class Observable {
    private observers: Array<(event: T) => void> = [];

    subscribe(observer: (event: T) => void): void {
        this.observers.push(observer);
    }

    notify(event: T): void {
        this.observers.forEach(observer => observer(event));
    }
}

// Uso con tipos concretos
type UserEvent = { type: 'login'; payload: { userId: string } } | { type: 'logout'; payload: null };

const userObservable = new Observable();
userObservable.subscribe(event => {
    if (event.type === 'login') {
        console.log(`Usuario ${event.payload.userId} ha iniciado sesión`);
    }
});
// TypeScript previene errores: userObservable.notify({ type: 'error', payload: 'msg' }); // Error de tipo

En este ejemplo, el genérico T restringe los eventos a tipos específicos, evitando notificaciones con datos incorrectos. Esto es crucial en libs donde los usuarios deben seguir contratos de tipos bien definidos.

Caso de estudio

Supongamos que eres Frontend Engineer en una empresa que desarrolla una app de e-commerce con una arquitectura compleja basada en micro-frontends. Necesitas un sistema de gestión de estado que sea tipo-seguro y escalable. Implementa el patrón State con TypeScript para manejar el estado del carrito de compras.

// Estados posibles del carrito
type CartState = 'empty' | 'adding' | 'full' | 'checkout';

class CartContext {
    private state: T;

    constructor(initialState: T) {
        this.state = initialState;
    }

    transition(newState: U): CartContext {
        // Lógica de transición con validación de tipos
        if (this.state === 'empty' && newState === 'adding') {
            return new CartContext(newState);
        }
        // Otras transiciones...
        throw new Error('Transición inválida');
    }

    getState(): T {
        return this.state;
    }
}

// Uso en la app
const cart = new CartContext('empty');
const updatedCart = cart.transition('adding'); // Válido
// const invalidCart = cart.transition('checkout'); // Error en tiempo de compilación si no es válido

Este enfoque asegura que las transiciones de estado sean tipadas y predecibles, reduciendo bugs en flujos complejos. En una lib, podrías exportar estos tipos para que otros desarrolladores los usen correctamente.

Errores comunes

  • Usar any en genéricos: Evita class Example, ya que pierdes los beneficios del tipado. En su lugar, define restricciones claras, como T extends EventData.
  • No tipar retornos de métodos en patrones: En el patrón Factory, asegúrate de que los métodos devuelvan tipos concretos, no any, para que TypeScript pueda inferir y validar usos.
  • Ignorar la inferencia de tipos: No sobre-tipes cuando TypeScript puede inferir; por ejemplo, en new Observable(), el genérico se infiere automáticamente.
  • Implementar patrones sin considerar arquitectura: No apliques patrones por moda; evalúa si resuelven un problema real en tu contexto, como la escalabilidad en micro-frontends.
  • Olvidar documentar contratos de tipos: En libs, documenta los tipos esperados para patrones, usando comentarios JSDoc o archivos .d.ts, para facilitar el uso por otros equipos.

Checklist de dominio

  • ¿Puedes implementar al menos 3 patrones de diseño (ej., Observer, Factory, Strategy) con genéricos en TypeScript?
  • ¿Sabes usar conditional types o mapped types para hacer patrones más flexibles, como en Builder o Proxy?
  • ¿Eres capaz de detectar y corregir errores de tipo en implementaciones de patrones existentes?
  • ¿Puedes diseñar una pequeña lib que exporte patrones tipados para reuso en proyectos?
  • ¿Entiendes cómo los patrones con TypeScript mejoran la mantenibilidad en arquitecturas complejas, como micro-frontends?
  • ¿Has aplicado patrones en proyectos reales, midiendo su impacto en reducción de bugs o mejora de performance?
  • ¿Puedes explicar a un compañero la diferencia entre un patrón clásico y su versión con tipado avanzado en TypeScript?

Implementa un Patrón Factory Tipado para una Librería de Componentes UI

En este ejercicio, crearás un patrón Factory con TypeScript avanzado para generar componentes de UI en una librería frontend. Sigue estos pasos:

  1. Define una interfaz genérica UIComponent con un método render() que devuelva un string (simulando HTML).
  2. Crea clases concretas como Button y Input que implementen UIComponent, con propiedades específicas tipadas (ej., label: string para Button).
  3. Implementa una clase ComponentFactory con un método genérico createComponent<T extends UIComponent>(type: string, props: ...): T que instancie el componente correcto basado en el tipo.
  4. Asegúrate de que TypeScript infiera los tipos de las propiedades y prevenga errores, como pasar un label a un Input.
  5. Prueba la factory creando un Button y un Input, y llama a sus métodos render() para ver el output.

Objetivo: Practicar el uso de genéricos y tipos para hacer un patrón Factory seguro y reutilizable en arquitecturas de libs.

Pistas
  • Usa un tipo union para los tipos de componente permitidos, como 'button' | 'input'.
  • Considera usar conditional types en las props para mapear el tipo de componente a sus propiedades específicas.
  • En la factory, usa una estructura de datos como un mapa para asociar tipos con clases constructoras.

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.