Inicio / Artículos / Tres patrones de TypeScript que mejoran la arquitectura de las aplicaciones React

Tres patrones de TypeScript que mejoran la arquitectura de las aplicaciones React

Aprende cómo los patrones Repository, Observer y Builder utilizan el sistema de tipos de TypeScript para crear bases de código más limpias y fáciles de mantener en React y Next.js.

763 palabras

Escribir en TypeScript no significa usarlo bien

Muchos desarrolladores añaden anotaciones de tipo a sus variables y consideran que ya han hecho suficiente. Pero la verdadera fortaleza de TypeScript radica en otro lugar: en los patrones estructurales que separan una base de código frágil de una que crece de manera ordenada con el tiempo.

Habiendo dedicado tiempo a desarrollar aplicaciones en producción con React y Next.js, hay algunos patrones que se destacan como verdaderos cambios revolucionarios. Estos no son ejercicios abstractos de libros de texto; son soluciones a problemas con los que realmente te encontrarás.

1. Patrón de Repositorio: Separa la obtención de datos de todo lo demás

Cuando tus componentes llaman directamente a fetch(), te expones a una reescritura dolorosa en cuanto la API cambie su estructura.

El Patrón de Repositorio encapsula el acceso a los datos detrás de una interfaz limpia:

// repositories/userRepository.ts
interface UserRepository {
  getById(id: string): Promise<User>;
  getAll(): Promise<User[]>;
}

export class ApiUserRepository implements UserRepository {
  async getById(id: string): Promise<User> {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
  }

  async getAll(): Promise<User[]> {
    const res = await fetch('/api/users');
    return res.json();
  }
}

Con esto en su lugar, sus componentes dependen de una abstracción en lugar de de una implementación concreta. Eso significa que puede reemplazar ApiUserRepository por una versión de simulación en sus pruebas sin tocar ningún código de interfaz.

2. Patrón Observer: Estado reactivo sin la sobrecarga de Redux

Redux cumple con su función, pero para estados que no son especialmente complejos, puede parecer como usar un martillo pesado para una tarea sencilla. Una implementación ligera de Observer basada en clases y construida sobre un emisor de eventos tipado ofrece una alternativa más simple:

// utils/eventBus.ts
type EventMap = {
  'user:loggedIn': { userId: string };
  'cart:updated': { itemCount: number };
};

class TypedEventBus {
  private listeners: Partial<{
    [K in keyof EventMap]: ((payload: EventMap[K]) => void)[]
  }> = {};

  on<K extends keyof EventMap>(event: K, cb: (payload: EventMap[K]) => void) {
    (this.listeners[event] ??= []).push(cb);
  }

  emit<K extends keyof EventMap>(event: K, payload: EventMap[K]) {
    this.listeners[event]?.forEach(cb => cb(payload));
  }
}

export const eventBus = new TypedEventBus();

Todo aquí está fuertemente tipado: no hay tipos any ocultos. Eso significa que no existe ambigüedad sobre qué formato de carga esperar un determinado listener.

3. Patrón Builder: Organizar la creación de objetos complejos

Si alguna vez has tenido dificultades para crear objetos de filtro, configuraciones de solicitudes API o esquemas de formularios repletos de condiciones anidadas, el Patrón Constructor aporta una claridad inmediata:

// builders/queryBuilder.ts
class QueryBuilder {
  private params: Record<string, string> = {};

  withPage(page: number) {
    this.params['page'] = String(page);
    return this;
  }

  withLimit(limit: number) {
    this.params['limit'] = String(limit);
    return this;
  }

  withSearch(term: string) {
    if (term.trim()) this.params['q'] = term;
    return this;
  }

  build(): string {
    return new URLSearchParams(this.params).toString();
  }
}

// Usage
const query = new QueryBuilder()
  .withPage(1)
  .withLimit(20)
  .withSearch('typescript')
  .build();
// → "page=1&limit=20&q=typescript"

El código resultante se lee de forma natural, las operaciones se concatenan de manera ordenada y es estructuralmente imposible ejecutar los pasos en el orden incorrecto.

Puntos clave

  • Patrón Repository: separa la capa de acceso a datos de la lógica de interfaz de usuario, siguiendo el principio de inversión de dependencias
  • Patrón Observer: permite una comunicación ligera y reactiva entre las partes de la aplicación sin necesidad de código genérico excesivo
  • Patrón Constructor: hace que la creación de objetos complejos sea al mismo tiempo legible y segura
  • Todos estos tres patrones se benefician enormemente de las interfaces y generics de TypeScript, lo que los hace notablemente más limpios que sus equivalentes en JavaScript puro

“TypeScript no se trata solo de agregar tipos: crea un diálogo entre cada capa de tu aplicación.” — Dan Vanderkam, autor de Effective TypeScript

Qué probar a continuación

Elige un patrón de este artículo y úsalo en un módulo real de tu código esta semana. Trabaja con una sección a la vez, y recuerda que estos patrones son herramientas, no dogmas.

Los mejores conjuntos de código no se definen por la cantidad de tipos que contienen, incluso en un proyecto TypeScript. Se definen por su intencionalidad: cada patrón está allí porque se ha ganado ese lugar.

Lecturas relacionadas

  • Cuando la IA escribe su app React pero salta los principios de código limpio — Aprenda siete hábitos de código limpio: DRY, responsabilidad única, cláusulas de protección y más, que el código React generado por IA suele violar, y cómo corregirlos.
  • Comprendiendo los canales de React: cómo las máscaras binarias codifican la prioridad de actualización — Aprenda cómo React codifica múltiples prioridades de actualización en un único número entero mediante máscaras binarias, y por qué las operaciones bit a bit reemplazan a las simples banderas booleanas para la programación de tareas.