CAP 05 · LEC 03·TypeScript
Unions e intersecciones: componer tipos
Los tipos union (|) e intersección (&) son las operaciones fundamentales para componer tipos en TypeScript. Con ellos puedes modelar relaciones complejas sin duplicar código.
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →
Tipos union (|)
Un tipo union significa "puede ser uno u otro". El valor puede ser de cualquiera de los tipos listados.
// Un parámetro que acepta número o string
function mostrarId(id: number | string): void {
console.log("ID:", id);
}
mostrarId(42); // "ID: 42"
mostrarId("abc-99"); // "ID: abc-99"
// Union de literales — solo esos valores exactos
type Direccion = "norte" | "sur" | "este" | "oeste";
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
type EstadoSemaforo = "rojo" | "amarillo" | "verde";
let dir: Direccion = "norte";
// dir = "arriba"; // ❌ Error
// Union con null/undefined (nullable)
type MaybeString = string | null;
type OptionalNumber = number | undefined;
function buscar(id: number): Usuario | null {
return encontrarEnBD(id) ?? null;
}Narrowing en unions
Cuando tienes una union, TypeScript requiere que estreches (narrow) el tipo antes de usar métodos específicos de uno de los tipos:
function formatear(valor: number | string): string {
// ❌ No puedes asumir que es string o number sin verificar
// return valor.toUpperCase(); // Error: number no tiene toUpperCase
if (typeof valor === "string") {
return valor.toUpperCase(); // ✅ Aquí TypeScript sabe que es string
}
return valor.toFixed(2); // ✅ Aquí sabe que es number
}
console.log(formatear("hola")); // "HOLA"
console.log(formatear(3.14159)); // "3.14"
// Narrowing con instanceof
function procesar(val: Date | string): string {
if (val instanceof Date) {
return val.toISOString(); // ✅ es Date
}
return new Date(val).toISOString(); // ✅ es string
}Salida
HOLA
3.14Unions discriminadas
El patrón más potente: agrega una propiedad literal (kind, type, status) para distinguir variantes:
type Círculo = {
kind: "circle";
radio: number;
};
type Rectángulo = {
kind: "rect";
ancho: number;
alto: number;
};
type Triángulo = {
kind: "triangle";
base: number;
altura: number;
};
type Forma = Círculo | Rectángulo | Triángulo;
function área(forma: Forma): number {
switch (forma.kind) {
case "circle":
return Math.PI * forma.radio ** 2;
case "rect":
return forma.ancho * forma.alto;
case "triangle":
return (forma.base * forma.altura) / 2;
default: {
const _: never = forma; // exhaustive check
throw new Error("Forma desconocida");
}
}
}
console.log(área({ kind: "circle", radio: 5 }).toFixed(2)); // "78.54"
console.log(área({ kind: "rect", ancho: 4, alto: 6 })); // 24
console.log(área({ kind: "triangle", base: 3, altura: 8 })); // 12Salida
78.54
24
12Tipos intersección (&)
Un tipo intersección significa "debe ser ambos a la vez". Combina todas las propiedades:
interface Persona {
nombre: string;
edad: number;
}
interface Empleado {
empresa: string;
cargo: string;
}
type EmpleadoPersona = Persona & Empleado;
const trabajador: EmpleadoPersona = {
nombre: "Ana",
edad: 30,
empresa: "TechCorp",
cargo: "Desarrolladora",
};
// Útil para mixins / composición
type ConTimestamp = {
creado: Date;
actualizado: Date;
};
type UsuarioCompleto = Persona & EmpleadoPersona & ConTimestamp;| Union (|) | Intersección (&) | |
|---|---|---|
| Significado | "uno u otro" | "ambos a la vez" |
| Propiedades disponibles | Solo las comunes a todos los tipos | Todas las propiedades de todos los tipos |
| Cuándo usar | Valor puede ser de tipos distintos | Combinar múltiples tipos en uno |
| Ejemplo | string | number | Persona & Empleado |