typescript-intersecciones

Uso de Intersecciones en TypeScript

Las intersecciones en TypeScript nos permiten combinar múltiples tipos en un solo tipo. Es decir, sirven para crear un tipo que suma las propiedades de dos tipos.

Sintaxis de intersecciones

Un tipo de intersección (Intersection Type) se define utilizando el operador &. El tipo resultante contiene todas las propiedades de los tipos combinados.

type TipoInterseccion = Tipo1 & Tipo2;

Donde Tipo1 y Tipo2 pueden ser cualquier tipo de objeto, interfaz, tipo primitivo, etc.

Vamos a verlo con un ejemplo,

type A = { a: number };
type B = { b: string };

type C = A & B;
// C es equivalente a { a: number; b: string }

En el ejemplo anterior,

  • El tipo C es una intersección de los tipos A y B
  • Esto significa que C tendrá tanto la propiedad a del tipo A como la propiedad b del tipo B.

Combinación de interfaces

Una de las formas más comunes de usar tipos de intersección es combinando interfaces. Esto es especialmente útil cuando queremos crear un objeto que tenga propiedades de varias interfaces:

interface Identificable {
    id: number; // Propiedad id de tipo number
}

interface Nombrable {
    nombre: string; // Propiedad nombre de tipo string
}

// Definimos un tipo Persona que es la intersección de Identificable y Nombrable
type Persona = Identificable & Nombrable;

const persona: Persona = {
    id: 1,
    nombre: "Luis"
};

console.log(persona.id); // Imprime: 1
console.log(persona.nombre); // Imprime: Luis

En este ejemplo,

  • Persona es un tipo de intersección que combina las propiedades de Identificable y Nombrable
  • Por lo tanto, persona debe tener tanto un id como un nombre

Extensión de tipos

Los tipos de intersección son útiles para extender tipos existentes (por ejemplo, si se quiere añadir propiedades adicionales a un tipo ya definido):

interface Base {
  id: number;
}

type ConNombre = Base & { nombre: string };

const item: ConNombre = {
  id: 1,
  nombre: "Item 1"
};

En este ejemplo, ConNombre extiende el tipo Base añadiendo una propiedad nombre.

Intersecciones con tipos genéricos

Las intersecciones pueden combinarse con tipos genéricos para crear tipos aún más flexibles y reutilizables.

function combinar<T, U>(obj1: T, obj2: U): T & U {
    return { ...obj1, ...obj2 };
}

const objeto1 = { nombre: "Luis" };
const objeto2 = { edad: 30 };

const objetoCombinado = combinar(objeto1, objeto2);

console.log(objetoCombinado.nombre); // Luis
console.log(objetoCombinado.edad); // 30

En este ejemplo,

  • La función combinar acepta dos objetos de tipos genéricos T y U
  • Retorna un objeto que es la intersección de ambos tipos.

Fijaros que es una forma elegante de destructurar dos objetos arbitrarios y combinarlos, manteniendo el tipado estático

Consideraciones y problemas

Intersecciones imposibles

Hay que tener cuidado al definir intersecciones de combinaciones de tipos primitivos, porque podemos dar lugar a tipos imposibles, que simplemente que no pueden ser satisfechos.

type Id = number;
type Nombre = string;

type Usuario = Id & Nombre;

const usuarioId: Usuario = 12345; // Error: no puede ser un número solo
const usuarioNombre: Usuario = "Luis"; // Error: no puede ser una cadena solo

En este ejemplo, el tipo Usuario es imposible, porque una variable no puede ser a la vez number y string.

Intersección con Tipos any

Cuando se intersecta un tipo con any, el resultado será any. Por ejemplo:

type A = { a: number };
type B = any;

type C = A & B; // C es de tipo any