Interfaz TypeScript versus tipo: diferencias reales, probadas con el compilador

  1. Inicio
  2. Blog
  3. Desarrollo de software

Desarrollo de software

Interfaz TypeScript versus tipo: Diferencias reales, probadas con el compilador

Las interfaces y los alias de tipos se superponen tanto que la elección a menudo parece arbitraria. Esto es lo que cada uno puede hacer y el otro no, confirmado por errores reales del compilador, además de una regla simple que su equipo puede seguir.

8 min de lectura
Interfaz TypeScript vs tipo: diferencias reales, probadas con el compilador

Puntos clave

  • Para formas de objetos simples, una interfaz y un alias de tipo son casi intercambiables.
  • Sólo type puede nombrar uniones, tuplas y otros tipos que no sean objetos.
  • Solo interface admite la fusión de declaraciones, donde se combinan dos declaraciones.
  • Una regla común: interfaces para formas de objetos, type para todo lo demás, aplicadas de manera consistente.

Pocas preguntas sobre TypeScript causan tanto debate como Interfaz de TypeScript versus tipo. Muchos artículos enumeran diferencias que solían ser importantes en versiones anteriores pero que ahora están desactualizadas. Entonces, en lugar de repetir afirmaciones, las probamos.

Escribimos un archivo practicando cada diferencia y lo verificamos con TypeScript 5.6.3 en modo estricto. Los errores del compilador que se citan a continuación provienen de esa ejecución. El comportamiento puede cambiar entre versiones, así que compruébelo con la versión de TypeScript que utiliza su proyecto. Si eres nuevo en el lenguaje, comienza con nuestro tutorial de TypeScript para principiantes.

La respuesta corta

Un interface describe la forma de un objeto y se puede ampliar o reabrir. Un alias type da un nombre a cualquier tipo, incluidos objetos, uniones, tuplas y tipos de funciones. Cuando ambos pueden funcionar, la diferencia es principalmente el estilo.

donde son iguales

Estas dos declaraciones describen formas idénticas:

interface User {
  id: number;
  name: string;
  email?: string;
}

type UserAlias = {
  id: number;
  name: string;
  email?: string;
};

const a: User = { id: 1, name: "Asha" };
const b: UserAlias = a;   // no error

TypeScript utiliza tipificación estructural, lo que significa que compara formas en lugar de nombres. Por eso se puede asignar un User a un UserAlias sin queja alguna. Ambos admiten propiedades opcionales, readonly propiedades, métodos y firmas de índice.

Interfaz versus tipo uno al lado del otro

Característica interfaz tipo
Formas de objetos si si
Sindicatos, como "a" | "b" No si
Tuplas y primitivas No si
Tipos mapeados y condicionales No si
Declaración de fusión si No, es un error
Extendiendo se extiende & intersección
Propiedades en conflicto Error en la declaración Puede convertirse silenciosamente en never
Usado con implements si Sí, si es un tipo de objeto.

¿Qué solo el tipo puede hacer?

Muchos tipos útiles no son formas de objetos en absoluto, y sólo un alias de tipo puede nombrarlos:

type Status = "pending" | "paid" | "shipped";   // union
type Point = [number, number];                    // tuple
type Handler = (event: string) => void;           // function type
type Keys = keyof User;                           // computed type
type ReadonlyUser = Readonly<User>;               // utility type

Los tipos de unión se encuentran entre las características más valiosas de TypeScript, porque el compilador rechaza cualquier valor fuera de la lista:

let s: Status = "paid";
s = "refunded";

// error TS2322: Type '"refunded"' is not assignable to type 'Status'.

Esto detecta errores tipográficos y valores obsoletos en el momento de la compilación, antes de que lleguen a producción.

Declaración de fusión

Si declara una interfaz con el mismo nombre dos veces, TypeScript las fusiona en una:

interface Settings {
  theme: string;
}
interface Settings {
  language: string;
}

const settings: Settings = { theme: "dark", language: "en" };   // no error

Intente lo mismo con un alias de tipo y el compilador se niega:

type Config = { theme: string };
type Config = { language: string };

// error TS2300: Duplicate identifier 'Config'.

La fusión es la forma en que las bibliotecas le permiten ampliar sus tipos. Por ejemplo, puede agregar una propiedad personalizada a la interfaz global Window o al objeto de solicitud de un marco, sin modificar la biblioteca. Eso es realmente útil.

La otra cara de la moneda es que la fusión puede ocurrir por accidente. Si dos archivos en un proyecto declaran una interfaz con el mismo nombre en el mismo ámbito, se combinan silenciosamente, lo que puede resultar confuso. Un alias de tipo hace que esa colisión sea un error.

se extiende vs intersecciones

Ambos le permiten construir sobre un tipo existente:

interface Admin extends User {
  permissions: string[];
}

type AdminAlias = UserAlias & { permissions: string[] };

Para propiedades compatibles, el resultado es el mismo. La diferencia se manifiesta con un conflicto. Cuando una interfaz intenta cambiar el tipo de propiedad de manera incompatible, el compilador lo informa de inmediato:

interface Base { id: number }
interface Broken extends Base { id: string }

// error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
//   Types of property 'id' are incompatible.
//     Type 'string' is not assignable to type 'number'.

Una intersección no se queja cuando se declara. En cambio, la propiedad en conflicto se convierte en never, un tipo que ningún valor puede tener. El error solo aparece más tarde, cuando intentas usarlo:

type Mixed = { id: number } & { id: string };
const m: Mixed = { id: 1 };

// error TS2322: Type 'number' is not assignable to type 'never'.

Ese error posterior es más confuso, ya que apunta fuera del error real. Ésta es una razón práctica para preferir extends al crear tipos de objetos.

Clases e implementos.

Una clase puede prometer hacer coincidir una forma usando implements, y esto funciona con una interfaz o un alias de tipo de objeto:

class Account implements User {
  constructor(public id: number, public name: string) {}
}

Esta compilado sin errores. Tenga en cuenta que implements solo verifica que la clase coincida. No agrega ninguna propiedad ni código. Una clase no puede implementar un tipo de unión, ya que no existe una forma única que coincida.

Tipos de utilidad y de solo lectura

Los tipos de utilidades como Readonly, Partial, Pick y Omit funcionan tanto con interfaces como con alias. El resultado es un nuevo tipo, que normalmente recibe un nombre con un alias de tipo:

const r: ReadonlyUser = { id: 3, name: "Dev" };
r.name = "Chitra";

// error TS2540: Cannot assign to 'name' because it is a read-only property.

Recuerde que readonly es solo una verificación en tiempo de compilación. En tiempo de ejecución, el objeto es JavaScript normal y aún se puede cambiar mediante código que omita los tipos.

Consejo: Los tipos desaparecen cuando TypeScript se compila en JavaScript. Los datos que llegan desde el exterior, como una respuesta API, no se verifican en tiempo de ejecución solo porque tienen un tipo. Valídelo, por ejemplo, con una biblioteca de esquemas. Nuestro tutorial de recuperación de API cubre el manejo seguro de respuestas de API.

Rendimiento del compilador

La propia guía de rendimiento del equipo de TypeScript sugiere preferir interfaces que usan extends en lugar de intersecciones grandes, porque el compilador puede almacenar en caché las relaciones de interfaz de manera más eficiente. En proyectos pequeños y medianos es poco probable que notes alguna diferencia. Puede ser importante en bases de código muy grandes con tipos profundamente compuestos. No lo medimos nosotros mismos.

¿Cuál deberías usar?

Una regla ampliamente utilizada, de acuerdo con las pautas del manual de TypeScript, es:

  • Utilice interface para formas de objetos, especialmente API públicas y cualquier cosa que implementen las clases u otras puedan extenderse.
  • Utilice type para uniones, tuplas, tipos de funciones y tipos asignados o calculados.
tu estas definiendo uso
Un modelo de datos, como Usuario o Pedido interfaz
Un conjunto de valores permitidos. type unión
Un par o triple de longitud fija type tupla
Una firma de devolución de llamada tipo
Ampliando los tipos de una biblioteca interface, mediante la fusión
Una variación de un tipo existente. type con un tipo de utilidad

Algunos equipos simplemente usan type en todas partes y eso también funciona bien. Lo que más importa es la coherencia, así que elige una convención, escríbela en tu guía de estilo y deja que un linter la haga cumplir. La regla ESLint consistent-type-definitions de mecanografiado-eslint puede hacer esto.

Errores comunes

  • Debate sin fin. Ambos están bien para los objetos, así que establece la regla una vez y sigue adelante.
  • Fusión accidental. La reutilización de un nombre de interfaz en el mismo ámbito los combina silenciosamente.
  • Intersecciones conflictivas. Crean never tipos con errores confusos más adelante.
  • Tipos de confianza en tiempo de ejecución. Se borran, así que valide los datos externos.
  • Usando any para silenciar errores. Desactiva la comprobación. Prefiera unknown y limítelo.

Cambiar entre ellos en una base de código existente

Cambiar un interface por un type, o al revés, suele ser seguro para formas de objetos simples, porque TypeScript compara los tipos por su estructura, no por sus nombres. Aún es necesario comprobar algunas cosas antes de cambiar:

  • La conversión de una interfaz a un alias de tipo elimina la fusión de declaraciones, por lo que cualquier segunda declaración con el mismo nombre se convierte en un error de identificador duplicado.
  • La conversión de un alias de tipo en una interfaz solo funciona para formas de objetos. Las uniones, tuplas y tipos mapeados deben permanecer como alias de tipo.
  • Los mensajes de error y los desplazamientos del editor pueden mostrar el tipo de forma diferente, lo que puede sorprender a los compañeros de equipo pero no cambia el comportamiento.

Realice el cambio en una solicitud de extracción enfocada, ejecute el compilador en modo estricto y su conjunto de pruebas, y evite mezclarlo con el trabajo de funciones para que los revisores puedan ver que nada más cambió.

Conclusión

La pregunta entre interfaz y tipo tiene una respuesta tranquila: se superponen para los objetos, cada uno tiene algunas habilidades únicas y la coherencia importa más que la elección. Utilice interfaces para formas y tipos para todo lo demás, a menos que su equipo prefiera lo contrario. Para ver las diferencias usted mismo, copie los ejemplos anteriores en un archivo y ejecute tsc --noEmit --strict en él.

Preguntas frecuentes

¿Cuál es la diferencia entre interfaz y tipo en TypeScript?

Ambos pueden describir formas de objetos. Solo el tipo puede nombrar uniones, tuplas y tipos calculados, mientras que solo la interfaz admite la fusión de declaraciones.

¿Debo usar una interfaz o un tipo para los accesorios de React?

Cualquiera de los dos funciona bien. Muchos equipos usan interfaces para accesorios, mientras que otros prefieren el tipografía para mantener la coherencia con las uniones. Elija uno y aplíquelo a lo largo del proyecto.

¿Puede un tipo extender una interfaz?

Sí, utilizando una intersección como tipo Admin = Usuario & {permisos: cadena[]}. Una interfaz también puede ampliar un alias de tipo de objeto.

¿La interfaz es más rápida que el tipo?

La guía de rendimiento de TypeScript favorece que la interfaz se extienda a grandes intersecciones en grandes bases de código. En proyectos típicos, es poco probable que la diferencia sea perceptible.

¿Qué es la fusión de declaraciones?

Es cuando dos interfaces con el mismo nombre en el mismo ámbito se combinan en una. Los alias de tipo no se pueden fusionar y los nombres duplicados provocan un error.

sigue aprendiendo

Trabajar con Stack Quora

¿Necesitas ayuda con pruebas, WordPress o SEO?

Ayudamos a los equipos a enviar mejor software y hacer crecer sus sitios. Cuéntenos en qué está trabajando y le sugeriremos un siguiente paso claro.

Sigue leyendo

Más guías sobre testing, IA y desarrollo moderno

¿Te gustó este artículo? Elige un tema y salta directo a nuestros tutoriales más recientes.

Inteligencia artificial

Explicación de las alucinaciones de la IA: por qué los modelos de lenguaje inventan cosas y cómo reducirlas

Se explicaron las alucinaciones de la IA: qué son, por qué los modelos de lenguaje las producen, tipos comunes, cómo reducirlas con conexión a tierr…

Leer artículo →
DevOps

Explicación de las variables de entorno: archivos .env, secretos y configuración bien hechos

Variables de entorno explicadas con ejemplos de Python que ejecutamos: leerlas y configurarlas, por qué cada valor es una cadena, archivos .env, her…

Leer artículo →
Desarrollo de software

Explicación de la limitación de velocidad de API: algoritmos, encabezados y manejo de errores 429

La limitación de la tasa de API se explica con una simulación que ejecutamos: depósito de tokens, ventana fija, ventana deslizante, 429 respuestas, …

Leer artículo →
Automatización de pruebas

Burlarse en Python: unittest.mock, patch y los errores que hacen que las pruebas mientan

Burlarse en Python con unittest.mock, probado: Mock y return_value, side_effect, parches donde se usa un nombre, especificación y autospec, fechas d…

Leer artículo →
Pruebas de software

Explicación de la cobertura del código: cobertura de línea versus sucursal y lo que realmente significa 100%

Cobertura del código explicada con una ejecución real de cobertura.py: cobertura de línea, rama y condición, lectura de un informe, por qué una cobe…

Leer artículo →
Bases de datos y SQL

DELETE vs TRUNCATE vs DROP en SQL: diferencias, reversión y cuándo usar cada uno

DELETE vs TRUNCATE vs DROP explicado con SQL probado: qué elimina cada uno, reversión, activadores, restablecimiento de identidad, velocidad y en qu…

Leer artículo →
Bases de datos y SQL

Vistas SQL explicadas: cree, use y evite los errores, con ejemplos

Vistas SQL explicadas con consultas que ejecutamos: creación de una vista, resultados en vivo, ocultación de columnas, rendimiento, vistas actualiza…

Leer artículo →
Desarrollo de software

Interfaz TypeScript versus tipo: diferencias reales, probadas con el compilador

Interfaz TypeScript versus tipo, verificado con el compilador real: qué pueden hacer ambos, qué puede hacer solo uno, fusión de declaraciones, exten…

Leer artículo →
Desarrollo de software

Python *args y **kwargs explicados: funciones flexibles con ejemplos claros

Python *args y **kwargs explicados con el código que ejecutamos: empaquetado y desempaquetado, orden de parámetros, argumentos solo de palabras clav…

Leer artículo →
Desarrollo de software

Funciones Lambda de Python: sintaxis, usos reales y cuándo no utilizarlas

Funciones lambda de Python explicadas con el código que ejecutamos: sintaxis, clasificación con clave, mapa y filtro, cierres, trampa de enlace tard…

Leer artículo →
Inteligencia artificial

¿Qué son los agentes de IA? Cómo funcionan, con un bucle de agente simple que puedes ejecutar

¿Qué son los agentes de IA? Una guía sencilla sobre el bucle del agente, las herramientas, la memoria y las barreras de seguridad, con un pequeño bu…

Leer artículo →
DevOps

Monitoreo versus observabilidad: la verdadera diferencia, con registros, métricas y seguimientos

Explicación del monitoreo versus observabilidad: fallas conocidas y desconocidas, registros, métricas y seguimientos, un recorrido por incidentes de…

Leer artículo →
DevOps

Docker Volumes versus Bind Mounts: persistir los datos del contenedor de la manera correcta

Se explican los volúmenes de Docker frente a los montajes de enlace: por qué desaparecen los datos del contenedor, volúmenes con nombre, montajes de…

Leer artículo →
Automatización de pruebas

TDD vs BDD: diferencias clave, una función creada en ambos sentidos y cuándo usar cada una

TDD vs BDD explicado con una característica de descuento construida en ambos sentidos: refactor rojo-verde con el código que ejecutamos, escenarios …

Leer artículo →
Desarrollo de software

Git merge vs rebase: diferencias, ejemplos y cuándo usar cada uno

Git merge vs rebase explicado con salida real del log, una tabla comparativa, la regla de oro del rebase y un flujo de trabajo seguro para equipos.

Leer artículo →
Desarrollo de software

List comprehension en Python: sintaxis, ejemplos y cuándo evitarla

List comprehension en Python explicada: sintaxis, filtros, if-else, bucles anidados, tiempos medidos y cuándo es mejor un bucle for normal.

Leer artículo →