Type Guards y Narrowing
Narrowing es uno de los poderes más inteligentes de TypeScript. Cuando tienes una variable de tipo string | number y entras a un if (typeof valor === "string"), TypeScript sabe automáticamente que dentro de ese bloque la variable es string. No tienes que hacer un cast manual.
TypeScript analiza el flujo del código y reduce (narrow) el tipo en cada rama, dándote exactamente las propiedades y métodos disponibles según el tipo en ese punto.
TypeScript entiende varios operadores para hacer narrowing: typeof para primitivos, instanceof para clases, el operador in para verificar si una propiedad existe en un objeto, y comparaciones de igualdad (=== null, === undefined). Cuando ninguno de estos es suficiente, puedes crear type predicates personalizados: funciones que retornan "valor is Tipo" y le indican a TypeScript que si la función retorna true, entonces la variable es de ese tipo específico.
// typeof narrowing
function procesar(valor: string | number) {
if (typeof valor === "string") {
return valor.toUpperCase(); // TypeScript sabe: string
}
return valor.toFixed(2); // TypeScript sabe: number
}
// instanceof narrowing
function manejarError(e: unknown) {
if (e instanceof Error) {
console.error(e.message); // TypeScript sabe: Error
} else if (typeof e === "string") {
console.error(e);
} else {
console.error("Error desconocido");
}
}
// in narrowing (propiedad existe en objeto)
interface Perro { ladrar(): void; }
interface Gato { maullar(): void; }
function hacerSonido(animal: Perro | Gato) {
if ("ladrar" in animal) {
animal.ladrar(); // TypeScript sabe: Perro
} else {
animal.maullar(); // TypeScript sabe: Gato
}
}
// Type predicate — función que hace narrowing
function esString(valor: unknown): valor is string {
return typeof valor === "string";
}
function esUsuario(obj: unknown): obj is Usuario {
return typeof obj === "object" && obj !== null && "id" in obj && "email" in obj;
}
// Exhaustiveness check con never
type Figura = { tipo: "circulo"; radio: number } | { tipo: "rect"; ancho: number; alto: number };
function area(f: Figura): number {
switch (f.tipo) {
case "circulo": return Math.PI * f.radio ** 2;
case "rect": return f.ancho * f.alto;
default: const _exhaustive: never = f; return _exhaustive;
}
}
Practica esta lección
Resuelve los ejercicios del curso de TypeScript en la app, gratis y con corrección automática.
Empezar a practicar