Lección 17 de 20 · Lectura y práctica
Planificar una Aplicación con Tipado desde Cero
Concepto clavePlanificar 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.
Del concepto a la práctica
Taller de esta lección
Planificar tipos antes de componentes ayuda a fijar las fronteras del gestor de tareas. Conviene separar la entidad persistida, el formulario de alta y los eventos, porque cada uno tiene campos distintos.
Ejemplo resuelto
type Task = { id: string; title: string; priority: "low" | "high"; done: boolean };
type NewTask = Omit<Task, "id" | "done">;
type TaskEvent = { type: "created"; task: Task } | { type: "completed"; id: string };Una alta no puede declarar done por accidente; el estado inicial se decide en una función.
Pruébalo
Crea una
NewTaskcon título y prioridad.Construye una
Taskasignandoidydone: false.Emite un evento
completed.Intenta incluir
tasken ese evento.
Comprueba tu resultado
La tarea final debe contener cuatro campos y el evento de finalización solo type e id.
Error frecuente: reutilizar Task para el formulario y hacer todos sus campos opcionales. Corrígelo con Omit; así cada frontera expresa su responsabilidad.
Omit y las comprobaciones de exceso de propiedades de un literal no impiden que una variable con más campos sea asignable estructuralmente. Una NewTask externa necesita validación de title y priority y selección de campos; la función de alta debe generar el id y fijar donefalse sin copiar done del formulario. Define además unicidad del id y la transición cuando ya estaba completada: esos contratos de ejecución no aparecen automáticamente por declarar TaskEvent.

Mapa de prácticaUna 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
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
anyounknownsin necesidad, perdiendo ventajas de TypeScript. Solución: Define tipos específicos; usaunknownsolo para datos externos con validación. - Duplicación de interfaces: Crear
UseryUserDTOseparados 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.ComponentTypepara 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
- He definido todas las entidades principales del dominio con interfaces o tipos.
- He utilizado tipos genéricos para crear variantes reutilizables (ej.,
PaginatedResponse). - He estructurado tipos en archivos lógicos (ej., carpeta
types/) y evitado ciclos de dependencia. - He modelado estados complejos (ej., formularios, drag-and-drop) con tipos unidos o intersecciones.
- He integrado tipos para librerías externas (ej., React Router, Redux) usando declaraciones o genéricos.
- He escrito tests de tipos con herramientas como
tsdpara verificar contratos. - 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:
- Crea una interfaz
Commentcon propiedades:id(string),content(string),authorId(string),timestamp(Date), yreplies(array de Comment). - Define un tipo genérico
ApiResponseque incluya campos paradata(T),status(200, 400, 500), ymessage(string opcional). - Modela el estado de la UI con un tipo unido:
CommentsUIStateque pueda ser'loading' | 'loaded' | 'error'. - Crea una función tipada
addReply(commentId: string, reply: Comment): ApiResponseque simule añadir una respuesta. - Escribe un ejemplo de uso en un archivo TypeScript, mostrando cómo se pasarían estos tipos a un componente React.
Pistas
- Usa
Partialpara permitir comentarios incompletos durante la creación. - Considera usar
Recordpara 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.
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.