Genéricos en TypeScript: La herramienta que necesitas conocer (y usar)


¿Te ha pasado que estás escribiendo una función y de repente te das cuenta de que necesitas la misma lógica para strings, para usuarios, para configuraciones, etc., etc.,? Terminas duplicando código por todos lados. Eso es deuda técnica acumulándose. Este artículo se me ocurrío porque me tocó revisar una codebase con 95% del código duplicado.

Pero regresando al tema. Los genéricos en TypeScript no son ningún truco de magia. Son una solución práctica para escribir código reutilizable y type-safe sin caer en el uso de any o duplicar funciones.

El concepto básico

Piensa en una máquina expendedora. Podrías construir una máquina diferente para cada producto: una para snacks, otra para bebidas, otra para lo que sea. O podrías diseñar una que se adapte a lo que le metas. Eso son los genéricos: una plantilla que no te amarra a un solo tipo.

¿Y qué es ese <T> que ves por ahí? Seguro lo has visto en varios lenguajes tipados.

T es un placeholder, un marcador de posición. Básicamente le dice a TypeScript: "cuando llames esta función, dime con qué tipo vas a trabajar y yo me adapto".




Ahora puedes hacer esto:


Sin duplicar código. Sin perder la seguridad de tipos. TypeScript sigue sabiendo exactamente qué hay dentro de cada caja. No se vuelve todo una caja negra (suele pasar bastante, primero Dios y tú saben  que hace el código, luego solo Dios 💀)


Restricciones: cuando tu genérico necesita reglas


A veces necesitas que T cumpla ciertas condiciones. Por ejemplo, si estás ordenando elementos y todos deben tener una propiedad nombre:


Si alguien intenta pasar un array de números, TypeScript lo detiene en tiempo de compilación. Nada de errores en runtime a las 2 de la mañana XD


Casos más avanzados

Puedes usar múltiples parámetros de tipo:



O incluso definir tipos por defecto:




Las desventajas

Los genéricos tienen su lado complicado. Añaden complejidad cognitiva. Para alguien que está aprendiendo, ver <T extends U where V> puede ser intimidante. Y si abusas de ellos, terminas con tipos imposibles de leer: Wrapper<Promise<Ref<T[]>>>.

Los mensajes de error también pueden ser confusos. A veces TypeScript te suelta algo como "Type 'X' is not assignable to type 'infer Y in conditional over mapped tuple'" y te quedas igual.

No todas las funciones necesitan genéricos. Si solo trabajas con strings, usa string directamente. No compliques las cosas sin razón.


¿Cuándo usarlos?

Úsalos cuando:

  • Construyas utilidades reutilizables: funciones como map, filter, cache, wrappers de resultados
  • Trabajes con clientes API y quieras mantener type safety entre diferentes endpoints
  • Desarrolles librerías de componentes: props de React, formularios, modales

No los uses cuando:

  • El tipo es fijo y no va a cambiar
  • Tu equipo está empezando con TypeScript (mejor mantenerlo simple)
  • No aportan valor real (si solo lo usas con un tipo, no tiene sentido)

Conclusión

Los genéricos no son algo que debas usar en todas partes. Pero cuando necesitas consistencia entre tipos diferentes (datos de usuario, objetos de configuración, respuestas de API) sin perder seguridad de tipos, son la herramienta indicada.

Piénsalo como una llave maestra: una llave, muchas puertas. Cada cerradura es diferente, pero el mecanismo es el mismo. No reconstruyes la puerta, diseñas la llave para que se adapte.

Úsalos donde la flexibilidad y la seguridad deben coexistir. No siempre, no en todas partes. Solo cuando tiene sentido.

No hay comentarios:

Con la tecnología de Blogger.