Los tipos genéricos en TypeScript resuelven una paradoja curiosa: el lenguaje nació para tipar lo que JavaScript no tipa, y aun así ofrece una herramienta para trabajar con datos sin un tipo fijo. Si estás migrando un proyecto de JavaScript a TypeScript, esta función te salva cuando no puedes forzar un tipo concreto.
La idea es simple: hay sistemas que no se adaptan fácilmente a valores tipados, y los genéricos te permiten mantener flexibilidad sin perder la ayuda del compilador.
¿Qué son los tipos genéricos en TypeScript?
Un genérico es un tipo que se define con la sintaxis <T> y acepta cualquier tipo de variable: numérica, cadena, booleana, etc. La letra T actúa como un placeholder que toma forma cuando invocas la función, clase o interfaz.
¿Qué significa la T en TypeScript? Es una convención para indicar un tipo genérico (Type). Puedes usar otra letra, pero T es el estándar más común.
La diferencia con any está en el control: con genéricos, TypeScript recuerda qué tipo entró y qué tipo debe salir, así que sigue avisándote si te equivocas.
¿Cómo crear una función genérica?
Antes de codear, conviene ordenar el repositorio. En el ejemplo se crea una carpeta llamada genericos (sin acento, para evitar problemas comunes en repos de GitHub) y dentro un archivo main.ts.
La función genérica se declara con <T> justo después del nombre, y recibe un parámetro de ese mismo tipo. Así puedes invocarla con cualquier dato sin reescribir lógica.
ts
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("mi output");
let output2 = identity<number>(42);
console.log("output1:", output1);
console.log("output2:", output2);
Fíjate en el detalle clave: estás llamando al mismo método dos veces, pero defines el tipo en cada invocación. Una vez es string, otra es number, y el código sigue funcionando sin tocar la firma de la función.
Para compilar y correr:
cd genericos
tsc main.ts
node main.js
Los dos outputs salen sin error sin importar el tipo de dato.
¿Cómo usar clases e interfaces genéricas?
Los genéricos no se quedan en funciones. También aplican a clases e interfaces, y ahí es donde su utilidad crece en proyectos reales.
Clase genérica con un ejemplo de Caja
Una clase genérica recibe el tipo entre <> y lo propaga a sus propiedades y métodos. En el ejemplo, la clase Caja<T> guarda un contenido del tipo que tú definas al instanciarla.
ts
class Caja<T> {
contenido: T;
constructor(valor: T) {
this.contenido = valor;
}
obtenerContenido(): T {
return this.contenido;
}
}
let cajaDeString = new Caja<string>("libros");
console.log("contenido de la caja de string:", cajaDeString.obtenerContenido());
Al ejecutar, verás contenido de la caja de string: libros. Y lo interesante: puedes crear una Caja<number> o Caja<boolean> con la misma clase, sin duplicar código.
¿Cuándo conviene usar genéricos en lugar de any? Cuando quieres flexibilidad pero sin perder validación. any apaga el chequeo de tipos; los genéricos lo conservan según el tipo que pases en cada uso.
Interfaces genéricas para mezclar conceptos
Un tip que vale oro: los genéricos también funcionan con interfaces. Si ya viste cómo se crean interfaces en clases anteriores, ahora te toca mezclar los tres conceptos: funciones genéricas, clases genéricas e interfaces genéricas.
Algunas combinaciones útiles para practicar:
- Una interfaz genérica que defina la forma de un contenedor.
- Una clase que implemente esa interfaz con un tipo concreto.
- Una función que reciba instancias de esa clase y devuelva su contenido.
Si te equivocas, TypeScript te muestra mensajes de error bastante claros que apuntan al lugar exacto donde se rompe el tipado. Esa retroalimentación es justo lo que hace que valga la pena combinar los tres antes de llevarlo a un proyecto real.
¿Por qué practicar antes de implementarlos en un proyecto real?
Mezclar funciones, clases e interfaces genéricas en ejercicios pequeños te deja claro cuándo te conviene cada uno. No es lo mismo necesitar una utilidad reutilizable (función) que modelar una entidad con estado (clase) o describir un contrato (interfaz).
Prueba con los tres tipos básicos (string, number, boolean) y observa qué errores aparecen cuando rompes el tipado a propósito. Esa es la mejor manera de leer los mensajes de TypeScript con calma.
¿Ya hiciste tu primera función genérica? Cuéntame en los comentarios cómo te fue al combinarla con clases e interfaces.