Planificar una Aplicación con Tipado desde Cero

Lectura
20 min~4 min lectura

Concepto clave

Planificar una aplicación con tipado desde cero significa diseñar primero los tipos antes de escribir código, estableciendo contratos claros entre componentes. Imagina construir un puente: primero defines los planos estructurales (tipos), luego colocas los materiales (implementación). En TypeScript avanzado, esto implica modelar el dominio completo con interfaces, tipos genéricos y utilidades como Pick, Omit o Partial para garantizar consistencia en arquitecturas complejas.

Este enfoque previene errores en tiempo de compilación, mejora la mantenibilidad y facilita la colaboración en equipos grandes. Por ejemplo, en un frontend con múltiples vistas y estados, definir tipos para props, estados y eventos desde el inicio evita "type drift" (deriva de tipos) donde las interfaces se desincronizan con la implementación real.

Cómo funciona en la práctica

Comienza identificando las entidades principales de tu aplicación. Para un e-commerce, podrían ser Product, User, Order. Define interfaces base:

interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
}

interface User {
  id: string;
  email: string;
  role: 'admin' | 'customer';
}

Luego, usa tipos genéricos para crear variantes. Por ejemplo, un tipo para productos en carrito:

type CartItem = T & {
  quantity: number;
  addedAt: Date;
};

// Uso:
const item: CartItem = {
  id: "123",
  name: "Laptop",
  price: 999,
  category: "Electronics",
  quantity: 1,
  addedAt: new Date()
};

Finalmente, estructura los módulos: crea archivos como types/product.ts, types/user.ts y exporta tipos para usarlos en componentes React o servicios.

Caso de estudio

Supongamos una app de gestión de tareas con arrastrar y soltar. Primero, modelamos el estado:

type TaskStatus = 'todo' | 'inProgress' | 'done';

interface Task {
  id: string;
  title: string;
  description?: string;
  status: TaskStatus;
  assigneeId?: string;
}

interface Column {
  id: string;
  title: string;
  taskIds: string[];
}

interface BoardState {
  tasks: Record;
  columns: Record;
  columnOrder: string[];
}

Usamos tipos para eventos de drag-and-drop:

type DragStartEvent = {
  type: 'task';
  taskId: string;
  sourceColumnId: string;
};

type DropEvent = DragStartEvent & {
  targetColumnId: string;
};

function handleDrop(event: DropEvent, state: BoardState): BoardState {
  // Lógica para actualizar estado tipado
  return updatedState;
}

Esto asegura que los componentes reciban datos consistentes, reduciendo bugs en interacciones complejas.

Errores comunes

  • Tipos demasiado amplios: Usar any o unknown sin necesidad, perdiendo ventajas de TypeScript. Solución: Define tipos específicos; usa unknown solo para datos externos con validación.
  • Duplicación de interfaces: Crear User y UserDTO separados sin herencia. Solución: Extiende interfaces base o usa tipos utilitarios: type UserDTO = Pick.
  • Ignorar tipos genéricos en librerías: No parametrizar componentes React con props tipados. Solución: Usa React.ComponentType para mayor seguridad.
  • Tipos desactualizados: Modificar implementación sin actualizar tipos. Solución: Integra verificación en CI/CD con tsc --noEmit.
  • No planificar estados asíncronos: Dejar tipos para loading/error sin definir. Solución: Usa tipos unidos: type AsyncState = { loading: true } | { data: T } | { error: string }.

Checklist de dominio

  1. He definido todas las entidades principales del dominio con interfaces o tipos.
  2. He utilizado tipos genéricos para crear variantes reutilizables (ej., PaginatedResponse).
  3. He estructurado tipos en archivos lógicos (ej., carpeta types/) y evitado ciclos de dependencia.
  4. He modelado estados complejos (ej., formularios, drag-and-drop) con tipos unidos o intersecciones.
  5. He integrado tipos para librerías externas (ej., React Router, Redux) usando declaraciones o genéricos.
  6. He escrito tests de tipos con herramientas como tsd para verificar contratos.
  7. He documentado tipos clave con comentarios JSDoc para mejorar la colaboración.

Diseñar tipos para un sistema de comentarios en tiempo real

Implementa un sistema de tipos para una funcionalidad de comentarios en tiempo real en una aplicación frontend. Sigue estos pasos:

  1. Crea una interfaz Comment con propiedades: id (string), content (string), authorId (string), timestamp (Date), y replies (array de Comment).
  2. Define un tipo genérico ApiResponse que incluya campos para data (T), status (200, 400, 500), y message (string opcional).
  3. Modela el estado de la UI con un tipo unido: CommentsUIState que pueda ser 'loading' | 'loaded' | 'error'.
  4. Crea una función tipada addReply(commentId: string, reply: Comment): ApiResponse que simule añadir una respuesta.
  5. Escribe un ejemplo de uso en un archivo TypeScript, mostrando cómo se pasarían estos tipos a un componente React.
Pistas
  • Usa Partial para permitir comentarios incompletos durante la creación.
  • Considera usar Record para almacenar comentarios por ID en el estado.
  • Para el tipo unido, define cada variante como un objeto con una propiedad discriminante (ej., { state: 'loading' }).

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.