Generics en TypeScript: para qué sirven y cómo usarlos para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿Qué son los Generics y por qué son importantes?

\n\n

Al estudiar TypeScript, rápidamente te encuentras con situaciones donde necesitas escribir una función o clase que pueda trabajar con diferentes tipos de datos. Sin herramientas especiales, tendrías que duplicar el código para cada tipo o usar any, lo que elimina toda la magia del tipado estricto. Aquí es donde los Generics (genéricos o plantillas) vienen al rescate.

\n\n

Generics es un potente mecanismo de TypeScript que permite crear componentes (funciones, clases, interfaces) que trabajan con varios tipos, manteniendo al mismo tiempo un tipado estricto. Imagina que escribes un contenedor universal que puede almacenar manzanas, libros y números, pero TypeScript sabe exactamente qué hay dentro en cada caso concreto.

\n\n

En este artículo, analizaremos para qué sirven los Generics, cómo declararlos y usarlos en proyectos reales. Aprenderás a hacer tu código más flexible, seguro y reutilizable.

\n\n

Fundamentos de Generics: sintaxis y primera función

\n\n

Comencemos con un ejemplo simple. Supongamos que necesitamos una función que recibe un argumento y lo devuelve (función identidad). Sin genéricos, escribiríamos así:

\n\n
function identity(arg: number): number {\n    return arg;\n}
\n\n

Pero esta función solo funciona con números. Para hacerla universal, podemos usar any:

\n\n
function identity(arg: any): any {\n    return arg;\n}
\n\n

El problema es que perdemos la información del tipo. TypeScript no sabe que devolverá exactamente el mismo tipo que se pasó. Aquí es donde los Generics entran en acción:

\n\n
function identity<T>(arg: T): T {\n    return arg;\n}\n\n// Uso:\nlet output1 = identity<string>("Hola"); // tipo string\nlet output2 = identity<number>(42);       // tipo number
\n\n

Analicemos la sintaxis:

\n
    \n
  • <T> — es el parámetro de tipo. Puedes usar cualquier letra, pero por convención se suele usar T (Type), U, V.
  • \n
  • arg: T — el argumento tiene el tipo T.
  • \n
  • : T después de los paréntesis — la función devuelve un valor de tipo T.
  • \n
\n\n

Ahora TypeScript sabe: si pasaste una cadena, la salida también será una cadena. Si es un número, será un número. La inferencia de tipos a menudo funciona automáticamente, por lo que puedes escribir simplemente identity("Hola") — TypeScript entenderá que T es string.

\n\n

Generics en funciones: trabajo con arreglos y restricciones

\n\n

Los genéricos se usan con frecuencia para trabajar con arreglos o colecciones. Consideremos una función que devuelve el primer elemento de un arreglo:

\n\n
function getFirst<T>(arr: T[]): T | undefined {\n    return arr[0];\n}\n\nconst numbers = [1, 2, 3];\nconst firstNumber = getFirst(numbers); // tipo number\n\nconst strings = ["a", "b", "c"];\nconst firstString = getFirst(strings); // tipo string
\n\n

Observa: T[] es una notación abreviada para Array<T>. La función funciona con cualquier arreglo, pero devuelve un elemento exactamente del tipo que almacena el arreglo.

\n\n

Restricciones (Constraints) con extends

\n\n

A veces es necesario que el tipo T tenga ciertas propiedades. Por ejemplo, queremos obtener la longitud de un objeto. No todos los tipos tienen la propiedad length, por lo que podemos imponer una restricción:

\n\n
interface HasLength {\n    length: number;\n}\n\nfunction getLength<T extends HasLength>(arg: T): number {\n    return arg.length;\n}\n\ngetLength("cadena");     // OK, la cadena tiene length\ngetLength([1, 2, 3]);    // OK, el arreglo tiene length\ngetLength(42);           // ¡Error! El número no tiene length

Blogs

Book Recommendations