Quiz: Tipado en APIs y Estado

Quiz
10 min~5 min lectura

Quiz Interactivo

Pon a prueba tus conocimientos

Concepto clave

El tipado en APIs y estado es fundamental para garantizar la integridad de datos en arquitecturas complejas. En TypeScript avanzado, esto va más allá de definir interfaces básicas; implica crear tipos que reflejen con precisión las relaciones entre datos, validaciones en tiempo de compilación y contratos claros entre componentes. Piensa en esto como los planos de un edificio: no solo muestran las habitaciones, sino también cómo se conectan, qué materiales se usan y qué cargas soportan.

En el contexto de APIs, el tipado profundo permite detectar errores antes de que lleguen a producción, como respuestas mal formadas o parámetros incorrectos. Para el estado, especialmente en aplicaciones frontend complejas, un buen tipado previene bugs sutiles relacionados con mutaciones no deseadas o estados inconsistentes. La analogía real sería un sistema de control de calidad en una fábrica: cada pieza debe cumplir especificaciones exactas antes de ensamblarse.

Cómo funciona en la práctica

Imagina que estás construyendo una API para un sistema de e-commerce. En lugar de usar tipos simples, defines tipos genéricos que se adaptan a diferentes escenarios. Por ejemplo:

type ApiResponse = {
  data: T;
  status: number;
  timestamp: string;
};

type PaginatedResponse = ApiResponse<{
  items: T[];
  total: number;
  page: number;
}>;

// Uso con un producto
type Product = {
  id: string;
  name: string;
  price: number;
  category: string;
};

const fetchProducts = async (page: number): Promise> => {
  // Lógica de fetch
};

Este enfoque asegura que, al consumir la API, TypeScript valide que la estructura de la respuesta coincida exactamente con lo esperado, incluyendo la paginación. Para el estado, podrías usar tipos discriminados para manejar diferentes estados de carga:

type State =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: string };

// Esto previene acceder a data cuando status no es 'success'
const handleState = (state: State) => {
  if (state.status === 'success') {
    console.log(state.data); // TypeScript sabe que data existe aquí
  }
};

Caso de estudio

Considera una aplicación de gestión de tareas con backend en Node.js y frontend en React. La API define endpoints para tareas, y el estado frontend maneja listas filtradas. Implementamos tipos compartidos entre cliente y servidor para consistencia:

TipoDescripciónEjemplo de Uso
TaskRepresenta una tarea individualGET /tasks/:id
TaskFiltersParámetros para filtrar tareasGET /tasks?status=completed
TaskStateEstado de tareas en el frontendGestión de listas en React

Un error común sería definir tipos separados para cliente y servidor, llevando a inconsistencias. En su lugar, usamos un monorepo con tipos compartidos:

// shared/types.ts
export type Task = {
  id: string;
  title: string;
  description: string;
  status: 'pending' | 'in-progress' | 'completed';
  dueDate: string;
  assigneeId: string;
};

export type TaskFilters = Partial> & {
  dueBefore?: string;
};

// frontend/state.ts
type TaskState = {
  tasks: Task[];
  filters: TaskFilters;
  loading: boolean;
};

// backend/api.ts
const getTasks = (filters: TaskFilters): Promise => {
  // Lógica del servidor
};

Esto garantiza que cambios en la estructura de Task se reflejen automáticamente en ambos lados, reduciendo bugs.

Errores comunes

  • Tipos demasiado amplios: Usar any o unknown sin restricciones en APIs, lo que elimina los beneficios de TypeScript. Solución: Definir tipos específicos y usar validación en runtime con librerías como Zod si es necesario.
  • Duplicación de tipos: Crear interfaces separadas para request y response que son casi idénticas. En su lugar, usa tipos utilitarios como Pick, Omit, o Partial para derivarlos.
  • Ignorar estados asíncronos: No tipar estados de carga o error en el frontend, llevando a condiciones no manejadas. Usa tipos discriminados como en el ejemplo de State.
  • No validar respuestas de API: Asumir que el backend siempre devuelve lo esperado. Implementa guards de tipo o esquemas de validación.
  • Tipos anidados sin estructura: Crear objetos con múltiples niveles sin tipos intermedios, dificultando el mantenimiento. Extrae subtipos reutilizables.

Checklist de dominio

  1. ¿Puedes definir tipos genéricos para respuestas de API que incluyan paginación o metadata?
  2. ¿Implementas tipos discriminados para manejar estados de carga, éxito y error en el frontend?
  3. ¿Compartes tipos entre frontend y backend para evitar inconsistencias?
  4. ¿Usas tipos utilitarios de TypeScript (e.g., Partial, Pick, Record) para reducir duplicación?
  5. ¿Validas respuestas de API en runtime en producción para casos extremos?
  6. ¿Documentas tus tipos con comentarios que expliquen restricciones o usos esperados?
  7. ¿Refactorizas tipos cuando cambian los requisitos, en lugar de parchearlos con any?

Implementa un sistema tipado para una API de usuarios con filtros avanzados

En este ejercicio, crearás tipos para una API de gestión de usuarios que soporte filtros complejos y respuestas paginadas. Sigue estos pasos:

  1. Define un tipo User con propiedades: id (string), name (string), email (string), role ('admin' | 'user' | 'guest'), y createdAt (string).
  2. Crea un tipo UserFilters que permita filtrar por role, name (búsqueda parcial), y createdAfter (fecha mínima). Usa Partial y tipos utilitarios.
  3. Implementa un tipo genérico PaginatedResponse que envuelva cualquier tipo de dato, incluyendo items (array del tipo), total (número), y page (número).
  4. Escribe una función fetchUsers que acepte UserFilters y page, y devuelva una Promise de PaginatedResponse. Simula la lógica con datos mock.
  5. En el frontend, define un tipo UserState usando tipos discriminados para manejar 'idle', 'loading', 'success' (con datos), y 'error'.

Entrega el código en un archivo TypeScript con comentarios explicando tus decisiones de diseño.

Pistas
  • Usa Partial> para los filtros que coinciden con propiedades de User.
  • Considera usar Record para metadata adicional en PaginatedResponse si es necesario.
  • Para UserState, asegúrate de que el tipo discriminado prevenga acceder a data cuando el estado no es 'success'.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.