TypeScript de Cero a Experto / Narrowing
Narrowing: enseñarle a TypeScript a razonar
Aquí empieza lo que hace a TypeScript sentirse inteligente. El NARROWING es la
capacidad del compilador de SEGUIR tu razonamiento: si dentro de un if
comprobaste que algo es un string, TS lo sabe y te trata ese valor como string
—solo ahí dentro. Es la técnica que convierte las uniones de "incómodas" en
"poderosas", y la usarás en cada archivo que escribas.
El detective que descarta sospechosos
typeof: distinguir primitivos
El guardia más común. typeof (el mismo de JavaScript) le dice a TS con cuál
primitivo estás tratando, y TS estrecha el tipo dentro del bloque:
function formatear(valor: string | number): string {
// aquí valor es string | number: solo lo común a ambos
if (typeof valor === "string") {
// 🔍 TS estrechó: aquí valor es string
return valor.trim().toUpperCase(); // métodos de string, seguros
}
// 🔍 por descarte, aquí valor es number
return valor.toFixed(2); // métodos de number, seguros
}
console.log(formatear(" hola ")); // HOLA
console.log(formatear(3.14159)); // 3.14Los valores que typeof distingue son los de JavaScript: "string", "number",
"boolean", "object", "undefined", "function", "symbol", "bigint". TS
usa esa comprobación para saber, línea por línea, qué tipo tiene el valor. Fuera
del if, vuelve a ser la unión; dentro, es el tipo estrechado.
Truthiness: descartar null y undefined
Un simple if (valor) estrecha muchísimo: elimina null, undefined, 0, ""
y false —todos los falsy. Para descartar la ausencia de valor, es la
herramienta más rápida:
function saludar(nombre: string | null | undefined): string {
if (nombre) {
// 🔍 aquí TS sabe que nombre es string (descartó null Y undefined)
return "Hola, " + nombre.toUpperCase();
}
return "Hola, invitado";
}
// ⚠️ cuidado con la trampa del 0 y el "" (del SC-02): 'if (valor)' también
// descarta 0 y "" por ser falsy. Si 0 o "" son VÁLIDOS para ti, no uses
// truthiness a secas: compara explícitamente.
function esValido(cantidad: number | null): boolean {
if (cantidad !== null) { // ✅ descarta SOLO null; el 0 sobrevive
return cantidad >= 0;
}
return false;
}if (nombre) es cómodo para descartar null/undefined, pero recuerda la
lección del supercurso de JavaScript: también descarta 0 y "". Si esos son
valores legítimos, compara explícitamente (!== null, !== undefined) en vez de
confiar en la truthiness. TS estrecha igual con ambas formas; tú eliges la que no
te muerda.
Equality narrowing: comparar estrecha
Comparar dos valores también estrecha: si x === y y TS conoce el tipo de y,
aprende algo sobre x:
function procesar(estado: "activo" | "inactivo" | "pausado") {
if (estado === "activo") {
// 🔍 aquí estado es exactamente "activo"
return "corriendo";
}
// aquí estado es "inactivo" | "pausado"
return "detenido";
}
// también funciona descartando: 'if (x !== undefined)' estrecha quitando undefined
function largo(texto: string | undefined): number {
if (texto !== undefined) {
return texto.length; // 🔍 texto es string
}
return 0;
}Tienes valor: number | null y escribes if (valor) { usar(valor) }. ¿Qué problema sutil puede tener?
Mini-reto
Estrecha con criterio: 1) escribe function medir(x: string | number | boolean): string que devuelva la longitud si es string, el doble si es number, y "sí/no" si
es boolean —usando typeof para cada rama; 2) agrega una guardia temprana para un
parámetro string | null que retorne antes si es null; 3) confirma en tu editor
que, tras cada chequeo, TS te ofrece SOLO los métodos del tipo estrechado. Ese
autocompletado que cambia según el if es el narrowing en vivo.
Qué sigue
typeof distingue primitivos. Pero tus datos también son objetos y clases —y para
esos hay otras dos herramientas de narrowing: instanceof (¿es una instancia de
esta clase?) y el operador in (¿tiene esta propiedad?). Las dominas en la
siguiente lección.