TypeScript de Cero a Experto / Narrowing
instanceof y el operador in: estrechar objetos
typeof distingue primitivos, pero tus datos también son objetos y clases —y ahí
no sirve (typeof de cualquier objeto es solo "object"). Para estrechar entre
objetos tienes dos herramientas: instanceof (¿viene de esta clase?) y el
operador in (¿tiene esta propiedad?). Con ellas, TS razona sobre la forma de tus
objetos.
Dos formas de identificar un objeto
instanceof: ¿de qué clase viene?
Para objetos creados con new (clases del módulo 11 de JS, o Error, Date,
Array), instanceof estrecha al tipo de esa clase:
class GastoError extends Error {
constructor(public campo: string) {
super("Gasto inválido: " + campo);
}
}
function manejar(error: unknown): string {
// el catch de JS da 'unknown' (o 'any'): estrechamos con instanceof
if (error instanceof GastoError) {
// 🔍 aquí error es GastoError → tiene .campo Y .message
return "Campo malo: " + error.campo;
}
if (error instanceof Error) {
// 🔍 aquí error es Error → tiene .message
return "Error: " + error.message;
}
return "Error desconocido";
}instanceof es EL patrón para manejar errores tipados (recuerda el módulo 15 de
JS): el catch te da un unknown, y instanceof lo estrecha a tu clase de error
—dándote acceso seguro a sus campos extra. Funciona con cualquier cosa creada por
una clase: tus errores, Date, Map, RegExp. La pregunta que responde: "¿este
objeto nació de este molde?".
El operador in: ¿tiene esta propiedad?
Cuando tus objetos NO son instancias de clases sino objetos planos (lo más común
en JS/TS moderno), los distingues por sus PROPIEDADES con in:
type Circulo = { radio: number };
type Rectangulo = { ancho: number; alto: number };
type Figura = Circulo | Rectangulo;
function area(figura: Figura): number {
if ("radio" in figura) {
// 🔍 tiene 'radio' → TS estrecha a Circulo
return Math.PI * figura.radio ** 2;
}
// 🔍 no tiene radio → por descarte, es Rectangulo
return figura.ancho * figura.alto;
}
console.log(area({ radio: 5 }).toFixed(2));
console.log(area({ ancho: 4, alto: 3 }));"radio" in figura pregunta si el objeto tiene esa propiedad, y TS lo usa para
estrechar: dentro del if, figura es un Circulo (el único con radio). Es
perfecto para objetos planos que se distinguen por qué campos tienen. Funciona,
pero fíjate que depende de recordar QUÉ campo es único de cada tipo —en la lección
4 verás una forma aún mejor y más robusta: las discriminated unions.
Tienes type Figura = { radio: number } | { lado: number } y quieres calcular el área. ¿Cuál estrecha correctamente entre las dos?
Mini-reto
Estrecha objetos: 1) crea type Perro = { ladra: boolean } y type Gato = { maulla: boolean }, y type Mascota = Perro | Gato; 2) escribe sonido(m: Mascota): string que use "ladra" in m para devolver "Guau" o "Miau"; 3) bonus:
escribe una función que reciba unknown, y con instanceof Date devuelva la
fecha formateada o "no es fecha". Sientes la diferencia entre preguntar por el
campo (in) y por el molde (instanceof).
Qué sigue
Los guardias que viste son los que TS trae de fábrica. Pero puedes crear los TUYOS: funciones que le enseñan a TypeScript "si esto devuelve true, entonces el valor es de tal tipo". Se llaman type guards personalizados, y son la llave para validar datos del mundo exterior con seguridad total.