Volver a TypeScript BásicoEl Operador
TypeScript
TypeScript Básico
Estrechamiento de Tipos (Type Narrowing)
TypeScript estrecha los tipos en tiempo de ejecución usando guardas de tipo, haciendo el código seguro y expresivo.
Estrechamiento con typeof
function formatear(valor: string | number): string {
if (typeof valor === "string") {
return valor.toUpperCase(); // TypeScript sabe: string aquí
}
return valor.toFixed(2); // TypeScript sabe: number aquí
}
Estrechamiento con instanceof
class ErrorApi extends Error {
constructor(public codigoEstado: number, mensaje: string) {
super(mensaje);
this.name = "ErrorApi";
}
}
function manejarError(error: unknown): string {
if (error instanceof ErrorApi) {
return `Error API ${error.codigoEstado}: ${error.message}`;
}
if (error instanceof Error) {
return error.message;
}
return "Error desconocido";
}
Uniones Discriminadas
// Cada caso tiene un campo literal único — el "discriminante"
type Figura =
| { tipo: "circulo"; radio: number }
| { tipo: "rectangulo"; ancho: number; alto: number }
| { tipo: "triangulo"; base: number; altura: number };
function area(figura: Figura): number {
switch (figura.tipo) {
case "circulo":
return Math.PI * figura.radio ** 2;
case "rectangulo":
return figura.ancho * figura.alto;
case "triangulo":
return 0.5 * figura.base * figura.altura;
}
// TypeScript sabe que el switch es exhaustivo — no se necesita default
}
Guardas de Tipo Personalizadas
interface Gato { maullar(): void }
interface Perro { ladrar(): void }
// Predicado de tipo: le dice a TypeScript lo que verifica la función
function esGato(mascota: Gato | Perro): mascota is Gato {
return "maullar" in mascota;
}
function hacerSonido(mascota: Gato | Perro) {
if (esGato(mascota)) {
mascota.maullar(); // TypeScript sabe: Gato
} else {
mascota.ladrar(); // TypeScript sabe: Perro
}
}
Funciones de Aserción
function asegurarDefinido<T>(valor: T, nombre: string): asserts valor is NonNullable<T> {
if (valor === null || valor === undefined) {
throw new Error(`Se esperaba que ${nombre} estuviera definido`);
}
}
function procesarUsuario(usuario: Usuario | null) {
asegurarDefinido(usuario, "usuario");
// TypeScript sabe que usuario es Usuario desde aquí
console.log(usuario.nombre);
}
El Operador in
interface Admin { rol: "admin"; permisos: string[] }
interface Invitado { rol: "invitado"; idSesion: string }
function saludarUsuario(usuario: Admin | Invitado) {
if ("permisos" in usuario) {
console.log(`Admin con ${usuario.permisos.length} permisos`);
} else {
console.log(`Sesión de invitado: ${usuario.idSesion}`);
}
}
Verificación de Exhaustividad
function nuncaDebeLlegar(valor: never): never {
throw new Error(`Caso no manejado: ${JSON.stringify(valor)}`);
}
function procesar(evento: "iniciar" | "detener" | "pausar") {
switch (evento) {
case "iniciar": return "Iniciado";
case "detener": return "Detenido";
case "pausar": return "Pausado";
default: return nuncaDebeLlegar(evento); // Error si falta un caso
}
}
Buena práctica: Prefiere uniones discriminadas +
switchsobre largas cadenasif/else. Añade unnuncaDebeLlegaren eldefaultpara detectar casos faltantes en tiempo de compilación.