Por qué la tipificación es una superpotencia, no una carga
\n\nImagine: escribe código, todo funciona, es feliz. Y luego — ¡pum! — aparece un error rojo en la consola: Cannot read property 'name' of undefined. ¿Le suena? Bienvenido al mundo de JavaScript sin tipos. Pero hay una buena noticia: el JavaScript moderno (y especialmente TypeScript) ofrece una herramienta poderosa: Generics (genéricos). No es solo "otra característica", sino una forma real de hacer el código más limpio, seguro y comprensible.
En este artículo, analizaremos qué es la tipificación en JavaScript, cómo funcionan los generics y por qué debería usarlos en cada proyecto. Sin teoría aburrida, solo ejemplos vivos y consejos prácticos.
\n\n¿Qué son los Generics y para qué sirven?
\n\nGenerics (tipos genéricos) es un mecanismo que permite escribir funciones, clases e interfaces que trabajan con diferentes tipos de datos, pero manteniendo una tipificación estricta. En términos simples: crea código "universal" que se adapta automáticamente al tipo de datos con el que trabaja.
\n\nSin genéricos, escribiría algo como:
\n\nfunction identity(arg: any): any {\n return arg;\n}\n\n¿El problema? any destruye toda la tipificación. Pierde información sobre el tipo y el compilador no podrá advertirle sobre un error. Con Generics es diferente:
function identity<T>(arg: T): T {\n return arg;\n}\n\n// Uso\nlet num = identity<number>(42); // tipo number\nlet str = identity<string>("Hola"); // tipo string\n\n¿Magia? No, solo tipificación inteligente. El tipo T es un marcador de posición que se reemplaza por el tipo real al llamar a la función.
Generics vs any: la batalla por la seguridad
\n\nMuchos principiantes preguntan: "¿Por qué complicarse con genéricos si puedo usar any?" La respuesta es simple: la seguridad de tipos no es solo "para que se vea bonito", sino para prevenir errores en tiempo de ejecución.
Mire este ejemplo sin Generics:
\n\nfunction getLength(data: any): number {\n return data.length; // si data es un número, dará error\n}\n\ngetLength(42); // undefined en tiempo de ejecución\n\n\nCon genéricos, indica explícitamente que la función trabaja con arrays o cadenas:
\n\nfunction getLength<T extends { length: number }>(data: T): number {\n return data.length;\n}\n\ngetLength([1,2,3]); // 3\ngetLength("hello"); // 5\n// getLength(42); // Error de compilación — un número no tiene length\n\n\n¿Ve la diferencia? El error se detecta al escribir el código, no en producción. Esta es la esencia de la tipificación avanzada.
\n\nEjemplos prácticos de Generics en JavaScript
\n\nVeamos algunos escenarios reales donde los genéricos realmente salvan el día.
\n\n1. Funciones genéricas para trabajar con arrays
\n\nSupongamos que escribe una utilidad para extraer el primer elemento de un array:
\n\nfunction first<T>(arr: T[]): T | undefined {\n return arr[0];\n}\n\nconst numbers = [10, 20, 30];\nconst firstNum = first(numbers); // tipo number | undefined\n\nconst strings = ["a", "b", "c"];\nconst firstStr = first(strings); // tipo string | undefined\n\n\nSin genéricos, tendría que escribir dos funciones separadas o usar any. Con la tipificación de JavaScript mediante Generics, una función funciona para todos los tipos.
2. Clases genéricas para almacenar datos
\n\nImagine que crea un envoltorio simple para almacenar un valor:
\n\nclass Box<T> {\n private content: T;\n\n constructor(initial: T) {\n this.content = initial;\n }\n\n getValue(): T {\n return this.content;\n }\n\n setValue(value: T): void {\n this.content = value;\n }\n}\n\n// Uso\nconst numberBox