Intersection Types en TypeScript

En la clase anterior vimos los literal types y ahora toca uno de esos conceptos que, cuando te hace click, empiezas a modelar datos como un profesional: los intersection types.

Un intersection type es la forma de decirle a TypeScript:

“Este tipo tiene que cumplir más de uno a la vez.”

¿Qué es un Intersection Type?

Si con un union type (A | B) el valor puede ser uno u otro, con un intersection type (A & B) el valor debe ser ambos.

Piensa en ello como combinar contratos.

Ejemplo: construir una entidad combinando tipos

Imaginemos estos tipos:

type User = {
  age: number
  email: string
  company: string
}

type UserId = {
  id: string | number
}

Ahora podemos combinarlos con &:

type UserWithId = User & UserId

El resultado es un tipo que exige todas las propiedades de ambos:

const user: UserWithId = {
  id: 1,
  age: 30,
  email: 'midu@dev.com',
  company: 'midudev',
}

Si te falta alguna propiedad, TypeScript te grita. Y si sobra alguna que no está en ninguno de los dos tipos, también.

¿Por qué no meter todo en un solo tipo?

Podrías hacer un único tipo con todas las propiedades, sí. Pero la gracia de las intersecciones es la composición:

  • User puede usarse en sitios donde no necesitas el id.
  • UserId puede reutilizarse con otras entidades (productos, comentarios, etc.).
  • UserWithId combina ambos solo donde haga falta.

Es el mismo principio de no repetirte que aplicamos en código normal.

Intersection vs Union: la diferencia clave

// Union: puede ser uno u otro
type StringOrNumber = string | number

// Intersection: debe cumplir ambos
type UserWithId = User & UserId
  • Union (|): el valor cumple al menos uno de los tipos.
  • Intersection (&): el valor cumple todos los tipos a la vez.

Caso práctico: extensión de entidades

Imagina que tienes una base común y quieres extenderla:

type BaseEntity = {
  id: string
  createdAt: Date
  updatedAt: Date
}

type Product = {
  name: string
  price: number
}

type ProductWithMeta = Product & BaseEntity

Ahora ProductWithMeta tiene name, price, id, createdAt y updatedAt. Todo validado, todo con autocompletado.

¿Qué pasa si los tipos tienen propiedades en conflicto?

Si combinas tipos que tienen la misma propiedad con tipos distintos, TypeScript resuelve la intersección de esos tipos:

type A = { value: string }
type B = { value: number }

type C = A & B
// value sería string & number → never (imposible de satisfacer)

Esto es algo a tener en cuenta: si dos tipos son incompatibles en una propiedad, el resultado será never y no podrás crear un valor válido.

Buenas prácticas

  • Usa intersecciones para componer tipos pequeños en lugar de crear tipos gigantes.
  • Nombra los tipos intermedios de forma semántica: UserWithId, ProductWithMeta, etc.
  • Si ves que un tipo crece demasiado, pregúntate si puedes descomponerlo con &.

Resumen

  • Los intersection types combinan varios tipos con &.
  • El valor resultante debe cumplir todos los tipos a la vez.
  • Son ideales para componer entidades sin repetir propiedades.
  • Si hay propiedades en conflicto, el resultado es never.
  • La diferencia con union (|): union es “uno u otro”, intersection es “todos a la vez”.