Interfaces en TypeScript: contratos y mejores usos
En esta clase entramos a uno de los conceptos más importantes (y más discutidos en Twitter) de TypeScript: las interfaces.
Las interfaces son otra forma de definir tipos, pero con una idea central muy clara: definir un contrato. Es decir, la forma que debe tener un objeto, incluyendo propiedades y métodos, para que el resto del código pueda confiar en ello sin miedo.
¿Qué es una interface?
Una interface define la estructura que debe cumplir un objeto:
- Qué propiedades tiene
- Qué tipos tienen esas propiedades
- Qué métodos existen y qué devuelven
interface User {
name: string
age: number
email?: string
greet(): string
}
En la práctica, para muchos casos se parece muchísimo a usar type para un objeto. Y ahí llega la pregunta clásica: vale… ¿entonces cuál uso?
Interface vs type: diferencias clave (las que de verdad importan)
Aunque se solapan en un montón de casos, hay diferencias prácticas:
Cosas que type puede hacer y interface no
- Union types: cuando algo puede ser “A o B” (por ejemplo,
User | null), esto es terreno detype. - Tuplas y ciertos tipos más “técnicos” también encajan mejor con
type. - Definir un tipo como primitivo directamente (
string,number, etc.) no es el punto fuerte de una interface — las interfaces están más pensadas para objetos.
// Esto solo puede hacerse con type
type StringOrNumber = string | number
type Coords = [number, number]
type ID = string
Cosas donde interface se siente más natural
- Modelar objetos complejos
- Extender contratos (herencia de forma clara)
- Trabajar con clases usando
implementspara obligar a cumplir un contrato
Regla práctica recomendada (para no vivir en el caos)
Una estrategia simple que funciona muy bien:
- Usa interfaces para objetos (especialmente si van a crecer o extenderse).
- Usa type para todo lo demás (unions, tuplas, utilidades, combinaciones raras, etc.).
¿Puedes usar type para todo? Sí.
¿Te va a explotar el proyecto? No.
¿Pero te quedará más ordenado si sigues una regla? También.
Métodos en interfaces: dos estilos válidos
Cuando defines métodos, TypeScript permite más de una sintaxis:
interface User {
// Estilo 1: como propiedad de tipo función
greet: (greeting: string) => string
// Estilo 2: como declaración de método
greet(greeting: string): string
}
Las dos son correctas, y puedes elegir la que te resulte más cómoda. Lo importante es que la interface puede describir métodos igual que propiedades.
Extender interfaces: contratos que se componen
Uno de los puntos fuertes de las interfaces:
interface Persona {
name: string
age: number
}
interface Usuario extends Persona {
email: string
role: 'admin' | 'user'
}
Incluso puedes extender de más de una interface:
interface Timestamps {
createdAt: Date
updatedAt: Date
}
interface Usuario extends Persona, Timestamps {
email: string
}
Esto hace que sea muy fácil reutilizar y mantener modelos sin duplicar campos por todas partes.
El “lado oscuro”: la fusión automática (declaration merging)
Aquí viene una rareza importante:
Si declaras la misma interface más de una vez en el mismo ámbito, TypeScript las fusiona automáticamente.
interface Config {
apiKey: string
}
interface Config {
baseUrl: string
}
// Config ahora tiene AMBAS propiedades: apiKey y baseUrl
const config: Config = {
apiKey: '123',
baseUrl: 'https://api.example.com',
}
- Puede ser confuso
- Puede ser peligroso si no lo esperas
- Pero tiene una utilidad real: extender tipos de librerías externas (module augmentation)
La idea no es abusar, pero sí saber que existe para no quedarte mirando el error como si fuera un bug de la Matrix.
Interfaces y clases: el caso donde brillan
El uso más potente y “natural” de las interfaces es cuando quieres que una clase cumpla un contrato:
interface Reproducible {
play(): void
pause(): void
stop(): void
}
class MusicPlayer implements Reproducible {
play() { console.log('Reproduciendo...') }
pause() { console.log('Pausado') }
stop() { console.log('Detenido') }
}
- La clase declara que implementa una (o varias) interfaces
- TypeScript obliga a que estén todos los métodos/propiedades requeridos
- Si falta algo, se queja (y con razón)
Esto es clave cuando quieres asegurar consistencia entre distintas implementaciones (por ejemplo, distintos reproductores, adaptadores, clientes, etc.).
Resumen
- Interfaces: contratos para objetos y especialmente útiles con clases y extensiones.
- Types: más flexibles para uniones, tuplas y tipos compuestos.
- Si quieres una regla fácil: interfaces para objetos, types para el resto.
- Las interfaces pueden fusionarse si las declaras varias veces (declaration merging) — a veces útil, a veces susto.
- Con
implements, las interfaces obligan a las clases a cumplir un contrato completo.