Interfaces vs type aliases en TypeScript: cuándo usar cada uno

Una de las primeras preguntas que surge al aprender TypeScript es si usar interface o type para describir la forma de un objeto. La respuesta corta es que en la mayoría de casos son intercambiables. La respuesta larga es que cada uno tiene capacidades exclusivas que conviene conocer para elegir bien.

Dónde son equivalentes

Describir la forma de un objeto funciona igual con ambos. El compilador trata los dos de la misma manera en el type-checking estructural:

interface Usuario {
  id: number;
  nombre: string;
  email?: string; // propiedad opcional
}

type Usuario = {
  id: number;
  nombre: string;
  email?: string;
};

// Ambas definiciones son compatibles con el mismo objeto literal
const u: Usuario = { id: 1, nombre: "Ana" };

También puedes extender ambos de formas similares. interface usa extends y type usa intersección con &:

// Con interface
interface Admin extends Usuario {
  rol: string;
}

// Con type alias
type Admin = Usuario & { rol: string };

Lo que solo puede hacer interface: declaration merging

Las interfaces permiten declararse varias veces con el mismo nombre y TypeScript las fusiona automáticamente. Esto es útil para extender tipos de librerías de terceros:

// En @types/express o en un fichero de declaraciones propio
interface Request {
  usuario?: Usuario;
}

// Más adelante, en otro fichero
interface Request {
  sesionId?: string;
}

// TypeScript fusiona ambas: Request ahora tiene usuario y sesionId

Los type aliases no permiten esto. Si declaras dos veces el mismo type obtendrás un error de nombre duplicado.

Lo que solo puede hacer type: union types y mapped types

Los type aliases pueden describir union types, tipos primitivos, tuplas y mapped types. Las interfaces no pueden:

// Union de tipos: solo con type
type Resultado = "ok" | "error" | "pendiente";
type IdONombre = number | string;

// Tupla tipada: solo con type
type Coordenada = [number, number];

// Mapped type: solo con type
type Opcional = { [K in keyof T]?: T[K] };

Si necesitas describir un tipo que puede ser una de varias formas posibles, type es la única opción.

Con clases y genéricos

Las clases implementan interfaces con implements. También pueden implementar type aliases si describen la forma de un objeto, aunque el uso con interfaces es más habitual y expresivo:

interface Serializable {
  serializar(): string;
}

class Producto implements Serializable {
  constructor(public nombre: string, public precio: number) {}

  serializar(): string {
    return JSON.stringify({ nombre: this.nombre, precio: this.precio });
  }
}

Tanto interface como type admiten parámetros genéricos:

interface Respuesta {
  datos: T;
  error: string | null;
}

type Respuesta = {
  datos: T;
  error: string | null;
};

La recomendación de la comunidad

La guía de estilo del equipo de TypeScript recomienda usar interface por defecto para describir objetos y reservar type para los casos en los que interface no alcanza: union types, tuplas, mapped types y aliases de tipos primitivos o complejos.

En la práctica, muchos proyectos mezclan los dos sin problema. Lo importante es ser consistente dentro del mismo proyecto y entender las diferencias para no sorprenderse cuando el compilador se queje. Si trabajas en librerías públicas, las interfaces favorecen que los consumidores puedan extender tus tipos mediante declaration merging, lo que es una ventaja.

Imagen: Pexels / Nemuel Sereti

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP