Genéricos en TypeScript: guía práctica para saber cuándo aplicarlos

En el día a día de un desarrollador frontend, TypeScript se ha convertido en una herramienta esencial para atrapar errores antes de que el código llegue al navegador. Uno de los conceptos que más potencia le da a este lenguaje son los genéricos. Pero, ¿cuándo deberías sacarle partido y por qué son tan útiles?
¿Qué son los genéricos?
Un genérico es una forma de definir componentes (funciones, clases, interfaces) que pueden trabajar con cualquier tipo, sin perder la seguridad que ofrece el tipado estático. En vez de escribir una función para number y otra para string, puedes escribir una única versión que acepte ambos y cualquier otro tipo que necesites.
Ventajas principales
- Reutilización: Evitas duplicar lógica.
- Seguridad: El compilador sigue controlando los tipos.
- Legibilidad: El código refleja la intención de que la estructura es flexible.
Cuándo usar genéricos
Los genéricos no son una solución mágica para todo. Aquí tienes algunas situaciones típicas donde realmente aportan valor:
- Funciones de utilidad que operan sobre colecciones, como
mapofilter. Si la función recibe un array deTy devuelve otro array deU, los genéricos permiten expresar esa relación. - Componentes de UI que aceptan datos de diferentes formas. Por ejemplo, un
Tableque recibe una lista de filas cuyo tipo varía según la página. - Servicios de API con respuestas tipadas. Puedes modelar la respuesta genérica y especializarla en cada endpoint.
- Clases de contenedores como
StackoQueue, donde el tipo de los elementos se define al crear la instancia.
Ejemplo práctico: una función de filtrado genérica
Supongamos que necesitas una utilidad que filtre cualquier array basándose en una condición. Con genéricos, la función queda totalmente tipada y reutilizable:
function filtrarArray<T>(arr: T[], predicado: (elem: T) => boolean): T[] {
const resultado: T[] = [];
for (const item of arr) {
if (predicado(item)) {
resultado.push(item);
}
}
return resultado;
}
// Uso con números
const pares = filtrarArray([1, 2, 3, 4], n => n % 2 === 0);
// Uso con objetos
interface Usuario { id: number; nombre: string; activo: boolean; }
const activos = filtrarArray([
{id:1, nombre:'Ana', activo:true},
{id:2, nombre:'Luis', activo:false}
], u => u.activo);
Observa cómo T se infiere automáticamente cuando llamamos a la función con un array de números, y cómo podemos especificarlo explícitamente para objetos.
Restricciones con extends
En ocasiones necesitas asegurarte de que el tipo genérico cumpla ciertas propiedades. Para ello se usan constraints con extends:
function obtenerPropiedad<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const usuario = { id: 1, nombre: 'María' };
const nombre = obtenerPropiedad(usuario, 'nombre'); // tipo string
La restricción K extends keyof T garantiza que la clave pasada exista en el objeto, evitando errores en tiempo de ejecución.
Genéricos en componentes React
Si trabajas con React, los genéricos también pueden ayudarte. Imagina un componente Select que recibe opciones de cualquier tipo, pero necesita saber cuál es la forma del valor seleccionado:
type Opcion<T> = { valor: T; etiqueta: string };
interface SelectProps<T> {
opciones: Opcion<T>[];
valorSeleccionado: T;
onChange: (valor: T) => void;
}
function Select<T>({ opciones, valorSeleccionado, onChange }: SelectProps<T>) {
return (
);
}
De esta forma, el componente es totalmente agnóstico al tipo del valor (puede ser number, string o incluso un objeto complejo) y sigue proporcionando autocompletado y detección de errores.
Buenas prácticas
- Prefiere la inferencia: deja que TypeScript deduzca el tipo siempre que sea posible; sólo explícitalo cuando la inferencia no sea clara.
- No abuses de los genéricos en funciones triviales; si la lógica es simple y el tipo es siempre el mismo, mantener la claridad es más importante que la flexibilidad.
- Documenta las restricciones con comentarios o con nombres descriptivos de los parámetros genéricos.
- Limita la profundidad de los genéricos. Anidar varios niveles (
<T, U, V>) puede dificultar la lectura.
Resumen
Los genéricos son una de las piezas clave que hacen de TypeScript un lenguaje potente para el desarrollo frontend. Úsalos cuando necesites reutilizar lógica, mantener seguridad de tipos y describir relaciones entre datos de forma declarativa. Con las técnicas mostradas —funciones reutilizables, restricciones y componentes tipados— podrás escribir código más limpio, menos propenso a errores y mucho más mantenible.