Lección 89 — Introducción a Genéricos
🧠 Concepto
Section titled “🧠 Concepto”Los genéricos permiten crear componentes (funciones, clases, interfaces, types) que funcionan con múltiples tipos sin perder la seguridad de tipos. Son la herramienta más poderosa de TypeScript para escribir código reutilizable.
El problema
Section titled “El problema”Sin genéricos, una función identity tendría que usar any (perdiendo información de tipo) o escribir una versión por cada tipo:
// Con any — pierde el tipofunction identity(arg: any): any { return arg;}
// Con genérico — preserva el tipofunction identity<T>(arg: T): T { return arg;}
const resultado = identity<string>('Hola'); // tipo: stringconst numero = identity(42); // tipo: 42 (inferido)Sintaxis básica
Section titled “Sintaxis básica”// Parámetro de tipo <T>function primera<T>(arr: T[]): T | undefined { return arr[0];}
// Tipo inferido automáticamenteconst primero = primera([1, 2, 3]); // tipo: number | undefinedconst primeroStr = primera(['a', 'b']); // tipo: string | undefinedMúltiples parámetros genéricos
Section titled “Múltiples parámetros genéricos”function par<A, B>(a: A, b: B): [A, B] { return [a, b];}
const resultado = par('edad', 30); // tipo: [string, number]Genéricos en interfaces y types
Section titled “Genéricos en interfaces y types”interface Caja<T> { contenido: T; abrir(): T;}
type Resultado<T> = { exito: boolean; datos: T; error?: string;};Genéricos en arrays
Section titled “Genéricos en arrays”let numeros: Array<number> = [1, 2, 3];let nombres: Array<string> = ['Ana', 'Luis'];
// Equivalente a:let numeros2: number[] = [1, 2, 3];💻 Ejemplo
Section titled “💻 Ejemplo”// Función genérica para el primer elementofunction primero<T>(arr: T[]): T | undefined { return arr.length > 0 ? arr[0] : undefined;}
console.log(primero([1, 2, 3])); // tipo: number | undefinedconsole.log(primero(['a', 'b', 'c'])); // tipo: string | undefined
// Función genérica para invertir un parfunction invertirPar<A, B>(par: [A, B]): [B, A] { return [par[1], par[0]];}
const invertido = invertirPar(['Ana', 30]); // tipo: [number, string]
// Interfaz genéricainterface Repositorio<T> { obtener(id: number): T; guardar(item: T): void; eliminar(id: number): void;}
class UsuarioRepositorio implements Repositorio<{ id: number; nombre: string }> { private items: { id: number; nombre: string }[] = [];
obtener(id: number) { return this.items.find(i => i.id === id)!; }
guardar(item: { id: number; nombre: string }) { this.items.push(item); }
eliminar(id: number) { this.items = this.items.filter(i => i.id !== id); }}
// Type genéricotype ParClaveValor<K, V> = { clave: K; valor: V;};
const entrada: ParClaveValor<string, number> = { clave: 'edad', valor: 30};📝 Ejercicio
Section titled “📝 Ejercicio”Escribe una función genérica mezclar<T, U>(a: T, b: U): T & U que combine dos objetos en uno solo (usa spread operator). Pruébala con:
- Un objeto
{ nombre: string }y otro{ edad: number }. - Un objeto
{ id: number }y otro{ activo: boolean }.
Verifica que el resultado tenga todas las propiedades.
⚠️ Nota
Section titled “⚠️ Nota”La convención es usar letras mayúsculas para los parámetros de tipo: T, U, V, K (key), V (value), E (element). Pero puedes usar nombres descriptivos: ResponseType, ItemType.
TS puede inferir el tipo genérico automáticamente en la mayoría de los casos. No necesitas escribir identity<string>('Hola'); basta con identity('Hola'). Especifica el tipo explícitamente solo cuando la inferencia no sea suficiente.