Type. Comparación con Interfaces
En TypeScript, 'type' es una palabra reservada que nos va a servir para crear tipos customizados.
Type
En TypeScript, "type" es una palabra reservada que nos va a servir para crear tipos customizados.
Veamos un ejemplo. Esto es llamado Type aliases (alias de tipos).
type ARG = string; // Esto es un alias de tipoAhora, ARG es un TIPO, y su tipo es literalmente string. Es sólo un alias, es decir, otra forma de escribir string.
Type combinado a Union type e Intersection type
Como sabemos, Union e Intersection son formas de asignar múltiples tipos a una variable. Union se hace con " | " e Intersection con " & ". Esto se puede combinar con Type así:
type AUTR = "auto" | true; // Union Type (AUBI puede ser "auto" o true)
type STNU = string | number; // Union Type (AUBI puede ser string o number)let valor: AUTR = "arbol"; // Esto va a dar error, "arbol" no es "auto" ni true
let valor2: STNU = "hola"; // Esto es válido, porque "true" es un stringEntonces, como vemos, tanto AUTR como STNU están funcionando como si fuesen alias. Es decir, la variable "valor" va a tener como tipo un string o un booleano, y valor2 un string o un number.
Combinándolo con Interfaces:
interface Persona {
nombre: string;
nota: number;
}
interface Profesor {
nombre: string;
legajo: string;
}
type AlumnoOProfesor = Alumno | Profesor;
const persona: AlumnoOProfesor = {
nombre: "Juan";
nota: 7
}Hacer eso funciona 100%. Ya que, como vimos en el apunte anterior, "persona" tiene el -shape- que tiene Alumno. Entonces entra perfectamente en el type AlumnoUProfesor.
Y a este punto, el Intellisense nos va a tomar a "persona" como si fuera de tipo Alumno. Y NO de AlumnoUProfesor, sino que de Alumno. Porque ya -infiere- que es de ese tipo. Eso es lógico, porque aumenta la precisión del tipo de la variable con la que trabajamos. Este proceso de -inferir- con más exactitud el tipo de una variable, se lo llama Narrowing (y está explicado en otro apunte, pero en realidad el concepto no es más que eso).
Y ojo, a la hora de hacer un método como este:
const metodoCualquiera = (persona: AlumnoOProfesor): void () => {
persona.nombre; // Esto SÍ lo reconoce, ya que "nombre" aparece en Alumno y en Profesor
// persona.nota; // Esto NO lo reconoce, se marcará el error
// persona.legajo; // Esto NO lo reconoce, se marcará el error
}Bien. Así funciona Union. Pero ahora vamos a ver la Intersection:
type AlumnoYProfesor = Alumno & Profesor;
const metodoCualquiera = (persona: AlumnoYProfesor) : void () => {
persona.nombre; // Esto SÍ lo reconoce.
persona.nota; // Esto SÍ lo reconoce.
persona.legajo; // Esto SÍ lo reconoce.
}Con el Type Intersection, se reconocen los campos aunque no estén en alguno de las dos interfaces. Esta diferencia respecto a Union es lógica: Con Union, "persona" puede ser Alumno O Profesor, entonces si tiene "nota", ya no puede ser Profesor. Entonces si no sabe qué es, simplemente reconoce a los elementos que estén tanto en Profesor como en Alumno. Porque esos son obvios que los tiene que tener. Pero "nota" y "legajo", no son obvios. Porque si es profesor, no tiene nota, y si es alumno, no tiene legajo.
En cambio, con Intersection, "persona" ESTÁ OBLIGADO a tener todo. Es decir, tiene que tener "nombre", "nota", y "legajo", porque sino, persona no sería de tipo AlumnoYProfesor. O sea que "persona" sí o sí tiene alguno de esos elementos. Así que el IntelliSense reconoce todos.
Comparación de "interface" con "type"
Las interfaces son ideales para ESTRUCTURAS DE OBJETOS, que posiblemente vayamos a extender en algún momento.
interface User {
name: string;
age: number;
}Características clave:
- Está pensado para definir la forma de un objeto.
- Se pude extender fácilmente (herencia con el "extends").
- Soporta declaración incremental (podemos agregar propiedades desde otro archivo).
- Se integra mejor con clases y blbliotecas como React o Angular.
interface Employee extends User {
role: string;
}Como vemos, es más semantico cuando definimos entidades (como User, Product, Post, etc.).
Los types, en cambio, son ideales para tipos más complejos o flexibles:
type ID = string | number;
type User = {
name: string;
age: number;
};Características clave:
- Permite uniones ( | ), intersecciones ( & ), y composición de tipos.
- Podemos combinar objetos, primitivos, funciones y más.
- Es más expresivo en tipos avanzados o utilitarios.
type ApiResponse = User | Error;
type Employee = User & { role: string };Entonces... ¿Cuándo usar cada uno?
| Situación | Usar interface | Usar type |
|---|---|---|
| Definir la forma de un objeto común | ✅ | ✅ (Ambos valen) |
| Necesitamos extenderlo o heredar estructuras | ✅ (con extends) | ⚠️ (con &) |
| Trabajamos con clases | ✅ | ❌ |
| Unión o combinación de tipos | ❌ | ✅ |
| Tipar funciones complejas | ⚠️ (Es posible) | ✅ |
| Necesitamos tipos condicionales o utilitarios | ❌ | ✅ |
| Queremos flexibilidad total | ❌ | ✅ |
Interfaces y comparación con Clases. Y Shapes
Una interface en TypeScript sirve para definir la forma (estructura) que debe tener un objeto. Es como un contrato: si un objeto 'implementa' una interface, está obligado a tener exactamente las propiedades y tipos definidos en ella. Pero es sólo eso. Es decir, no tienen lógica interna, es sólo marcar una estructura a respetar.
Tipos Genéricos
El tipo genérico <T> en TypeScript es una poderosa característica que permite escribir funciones, clases e interfaces de una manera que funciona con múltiples tipos de datos, manteniendo el código seguro y reutilizable.