Comprendiendo los Genéricos: Parámetros de Tipo
En JavaScript, pasamos datos como argumentos a funciones. En TypeScript, podemos pasar tipos como argumentos. A esto se le llama genérico (generic). Un genérico actúa como un contenedor de tipos flexible, a menudo representado con <T> (donde T denota un Tipo), permitiendo que una estructura de código se adapte a múltiples implementaciones de tipo con seguridad.
Aplicación Práctica en Funciones Utilitarias
Consideremos una función para recuperar datos del almacenamiento local del navegador. Sin genéricos, tendríamos que elegir entre tipos rígidos o el inseguro any.
Implementación con genéricos:
// Declaración de una función genérica
function recuperarDatoAlmacenado<T>(clave: string): T | null {
const valorBruto = localStorage.getItem(clave);
if (!valorBruto === null) return null;
try {
return JSON.parse(valorBruto) as T;
} catch {
return null;
}
}
// Definición de tipos específicos para el negocio
interface DatosSesion {
idSesion: string;
caducidad: number;
}
interface PreferenciasUI {
tema: 'claro' | 'oscuro';
idioma: string;
}
// Uso: el tipo se infiere o se especifica explícitamente.
const sesionActual = recuperarDatoAlmacenado<DatosSesion>('sesion');
const temaActual = recuperarDatoAlmacenado<PreferenciasUI>('preferencias');
Arquitectura Clave: Modelado de Respuestas de API Genéricas
Las APIs web suelen devolver respuestas con un formato común que incluye metadatos (como código de estado y mensaje) y un cuerpo de datos (data) cuya estructura varía. Definir una interfaz genérica evita duplicar tipos para cada endpoint.
// Interfaz genérica base para todas las respuestas de la API
interface RespuestaAPI<TPayload> {
codigoHttp: number;
mensaje: string;
datos: TPayload;
}
// Tipos de datos específicos del dominio
interface PerfilUsuario {
uuid: string;
nombreCompleto: string;
email: string;
}
interface InstanciaServicio {
nombreIntancia: string;
region: string;
estadoOperativo: 'en_linea' | 'mantenimiento';
metrics: { cpu: number; ram: number };
}
// Funciones que consumen la API usando los tipos genéricos
async function obtenerPerfil(): Promise<RespuestaAPI<PerfilUsuario>> {
const respuesta = await fetch('/api/perfil');
return respuesta.json();
}
async function listarInstancias(): Promise<RespuestaAPI<InstanciaServicio[]>> {
const respuesta = await fetch('/api/instancias');
return respuesta.json();
}
La firma de tipo RespuestaAPI<InstanciaServicio[]> es elocuente por sí sola: comunica que la respuesta contendrá un array de objetos de tipo InstanciaServicio dentro del campo datos.
Extensión Avanzada: Genéricos Anidados para Pgainación
Para manejar respuestas paginadas, podemos anidar genéricos. Se crea una estructura genérica que garantiza que datos sea siempre una lista.
// Estructura genérica para respuestas paginadas
interface RespuestaPaginada<TElemento> {
elementos: TElemento[];
totalElementos: number;
paginaActual: number;
tamanoPagina: number;
}
// Modelo de negocio específico
interface ModeloInteligenciaArtificial {
identificadorModelo: string;
proveedor: string;
ventanaContextoMax: number;
}
// Combina los genéricos: una respuesta de API que contiene una lista paginada
async function obtenerModelos(
pagina: number
): Promise<RespuestaAPI<RespuestaPaginada<ModeloInteligenciaArtificial>>> {
const res = await fetch(`/api/modelos?pagina=${pagina}`);
return res.json();
}
Esta construcción anidada RespuestaAPI<RespuestaPaginada<...>> proporciona una descripción completa y segura del contrato de tipo, mejorando drásticamente la mantenibilidad y autodocumentación del código.