Type Narrowing en TypeScript

En esta clase nos metemos con una técnica clave para escribir TypeScript sin pelearte con el compilador: el type narrowing (estrechamiento de tipos). La idea es simple: partir de un tipo amplio (por ejemplo, una unión) y reducirlo a uno más específico usando comprobaciones.

Qué es el type narrowing

Type narrowing es la técnica de reducir un tipo más amplio a uno más concreto mediante comprobaciones en tiempo de ejecución que TypeScript sabe interpretar.

Ejemplo típico: un valor que puede ser number | string.

function procesar(valor: number | string) {
  // aquí: valor es number | string
}

Narrowing con typeof

Cuando trabajas con primitivas, typeof es el clásico.

function procesar(valor: number | string) {
  if (typeof valor === 'number') {
    // aquí: valor es number
    return valor.toFixed(2)
  }

  // aquí: valor es string
  return valor.toUpperCase()
}

Esto funciona porque al comprobar typeof, TypeScript descarta tipos automáticamente y te deja el correcto dentro de cada rama.

Narrowing con truthy/falsy

No todo narrowing es typeof. Cuando un valor puede ser string | null | undefined, un simple if (valor) ya descarta null y undefined (porque son falsy).

function imprimirMensaje(mensaje: string | null | undefined) {
  if (mensaje) {
    // aquí: mensaje es string
    console.log(mensaje.trim())
    return
  }

  // aquí: mensaje es null | undefined
  console.log('No hay mensaje')
}

Esto te ahorra dos comprobaciones de golpe.

Operator narrowing con in

Para objetos, puedes estrechar mirando si existe una propiedad o método usando in.

type Pez = { nombre: string; nadar: () => void }
type Pajaro = { nombre: string; volar: () => void }
type Perro = { nombre: string; ladrar: () => void }

type Animal = Pez | Pajaro | Perro

function mover(animal: Animal) {
  if ('nadar' in animal) {
    // animal es Pez
    animal.nadar()
    return
  }

  if ('volar' in animal) {
    // animal es Pajaro
    animal.volar()
    return
  }

  // animal es Perro
  animal.ladrar()
}

Solo por comprobar “si tiene nadar, TypeScript sabe que estás ante un Pez.

Narrowing con instanceof

Cuando una variable puede venir como Date | string, instanceof es el recurso típico para fechas (y para errores, clases, etc.).

function formatDate(value: Date | string) {
  if (value instanceof Date) {
    // value es Date
    return value.toISOString()
  }

  // value es string
  return new Date(value).toISOString()
}

De nuevo: comprobación sencilla, tipo estrechado, y código más seguro.

Idea clave para quedarte

El type narrowing no es “aprenderte typeof. Es entender que TypeScript te acompaña cuando descartas posibilidades: por tipo, por truthiness, por propiedades, por instancias, etc. Tú vas cerrando puertas y el compilador hace el resto.

Resumen

  • Type narrowing es reducir un tipo amplio a uno concreto con comprobaciones.
  • typeof funciona para primitivas: string, number, boolean, etc.
  • Truthy/falsy descarta null y undefined de un plumazo.
  • in comprueba si un objeto tiene una propiedad y estrecha entre tipos de unión.
  • instanceof funciona con clases y constructores como Date, Error, etc.
  • TypeScript te acompaña: tú descartas opciones y él infiere el tipo correcto.