Lección 7 de 20 · Lectura y práctica
Ejercicio: Diseñar un Sistema de Notificaciones Tipado
Tipo de lección: VideoConcepto claveEn arquitecturas complejas, los sistemas de notificaciones requieren un tipado estricto que garantice consistencia y seguridad en tiempo de compilación. Los patrones genéricos en TypeScript.
Del concepto a la práctica
Taller de esta lección
Un mapa discriminado evita mezclar campos de canales distintos. Para correo necesitas destinatario y asunto; para una alerta interna basta un usuario y un código visible.
Ejemplo resuelto
type Notice =
| { channel: "email"; to: string; subject: string }
| { channel: "inapp"; userId: string; code: number };
function label(n: Notice) {
return n.channel === "email" ? `${n.to}: ${n.subject}` : `${n.userId}#${n.code}`;
}
console.log(label({ channel: "inapp", userId: "u7", code: 41 })); // u7#41Pruébalo
Añade un aviso de correo válido.
Ejecuta
labelpara ambos canales.Intenta añadir
subjectainappcomo único dato.Comprueba el diagnóstico del compilador.
Comprueba tu resultado
Las salidas deben ser u7#41 y ana@example.test: Aviso.
Error frecuente: declarar todos los campos como opcionales en un único objeto. Corrígelo usando una unión discriminada; el campo channel permite estrechar el tipo y obliga a completar solo los datos correspondientes.
Usa literalmente {channel:"email",to:"ana@example.test",subject:"Aviso"} para reproducir la segunda salida. La función solo genera una etiqueta y no envía correo ni muestra una notificación real. Una unión discriminada comprueba la coherencia del código local; un objeto recibido por red requiere validar channel y los campos del canal antes de llamar label. Si añades una variante, actualiza también las ramas de renderizado.

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
Tipo de lección: Video
Concepto clave
En arquitecturas complejas, los sistemas de notificaciones requieren un tipado estricto que garantice consistencia y seguridad en tiempo de compilación. Los patrones genéricos en TypeScript permiten crear abstracciones reutilizables que se adaptan a diferentes tipos de notificaciones sin perder información de tipos.
Imagina un sistema postal moderno: cada tipo de paquete (urgente, certificado, internacional) tiene requisitos específicos de etiquetado, seguimiento y entrega. Los genéricos actúan como plantillas inteligentes que aseguran que cada "paquete" de notificación cumpla con sus reglas específicas antes de enviarse.
Cómo funciona en la práctica
Comencemos con una interfaz base para notificaciones:
interface NotificationBase {
type: T;
timestamp: Date;
priority: 'low' | 'medium' | 'high';
} Ahora creamos un tipo genérico que extiende esta base:
type TypedNotification = NotificationBase & {
payload: P;
metadata: {
source: string;
version: number;
};
};
// Implementación concreta
interface EmailPayload {
recipient: string;
subject: string;
body: string;
attachments?: string[];
}
type EmailNotification = TypedNotification<'email', EmailPayload>; Este patrón permite crear notificaciones tipadas para diferentes canales (email, push, SMS) manteniendo consistencia estructural.
Caso de estudio
En una plataforma de e-commerce, necesitamos notificaciones para:
- Confirmación de pedido (email)
- Alerta de stock bajo (push)
- Recordatorio de carrito abandonado (SMS)
Implementamos un sistema centralizado:
class NotificationSystem {
private notifications: TypedNotification[] = [];
send(notification: TypedNotification): void {
// Validación de tipos en tiempo de compilación
this.notifications.push(notification);
this.process(notification);
}
private process(notification: TypedNotification): void {
switch(notification.type) {
case 'email':
// TypeScript sabe que payload es EmailPayload aquí
const emailPayload = notification.payload as EmailPayload;
console.log(`Enviando email a: ${emailPayload.recipient}`);
break;
case 'push':
// Lógica específica para push
break;
}
}
} El tipado profundo previene errores como enviar un SMS con datos de email, detectándolo durante el desarrollo, no en producción.
Errores comunes
- Any abuse: Usar
anyen genéricos destruye la seguridad de tipos. En lugar deTypedNotification, define interfaces específicas para cada payload. - Over-generification: Crear genéricos demasiado abstractos que pierden utilidad. Cada parámetro genérico debe tener una responsabilidad clara.
- Type assertion incorrecta: Usar
assin validación previa. Implementa type guards para conversiones seguras. - Ignorar constraints: No definir límites (
extends) en genéricos, permitiendo tipos incompatibles. - Mutable arrays tipados: Permitir modificación directa de arrays que deberían ser inmutables para mantener consistencia de tipos.
Checklist de dominio
- ¿Puedes crear un tipo genérico que acepte solo notificaciones con prioridad alta o media?
- ¿Implementaste type guards para diferenciar entre tipos de notificación en runtime?
- ¿Usas conditional types para transformar payloads basado en el tipo de notificación?
- ¿Aplicaste el principio de sustitución de Liskov en tu jerarquía de notificaciones?
- ¿Validaste que los genéricos mantengan covarianza/contravarianza donde sea necesario?
- ¿Documentaste con comentarios JSDoc los constraints de cada parámetro genérico?
- ¿Creaste tests que verifiquen el tipado en casos límite (edge cases)?
Diseña un sistema de notificaciones con tipos condicionales y mapeados
Objetivo
Crear un sistema de notificaciones tipado que use tipos condicionales de TypeScript para transformar automáticamente los payloads basado en el canal de envío.
Pasos
- Define una unión de tipos para los canales:
'email' | 'push' | 'sms' | 'in-app' - Crea interfaces específicas para cada tipo de payload:
- EmailPayload: con subject, body, attachments opcional
- PushPayload: con title, body, deepLink
- SMSPayload: con phoneNumber, message
- InAppPayload: con userId, component, data
- Implementa un tipo condicional que mapee cada canal a su payload correspondiente:
type PayloadForChannel= C extends 'email' ? EmailPayload : C extends 'push' ? PushPayload : C extends 'sms' ? SMSPayload : InAppPayload; - Crea una clase
NotificationDispatchercon un método genérico:send(channel: C, payload: PayloadForChannel ): void - Implementa validación que rechace combinaciones incorrectas (ej: enviar SMSPayload con channel 'email')
- Añade logging tipado que muestre propiedades específicas según el canal
Requisitos técnicos
- Usa tipos mapeados para crear configuraciones por canal
- Implementa al menos un tipo utilitario propio (ej:
MakeOptional<T, K>) - Incluye manejo de errores con tipos específicos
- Exporta tipos para uso en otros módulos
Pistas
- Usa 'extends' en genéricos para constraint los canales válidos
- Considera usar 'never' en tipos condicionales para casos no manejados
- Prueba tu tipado intentando pasar combinaciones inválidas y verificando que TypeScript las rechace
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.