DEV Community

Cover image for Generic Types en TypeScript, desarrollo reutilizable y adaptable
Joan Peramás Ceras
Joan Peramás Ceras

Posted on

Generic Types en TypeScript, desarrollo reutilizable y adaptable

Como desarrolladores frontend, comprendemos la importancia de crear componentes reutilizables y adaptables. Los Generic Types de TypeScript vienen al rescate, permitiéndonos escribir código que funcione perfectamente con varios tipos de datos sin sacrificar la seguridad de tipos.


¿Qué son los Tipos Genéricos?

Imagina una función que pueda manejar tanto cadenas de texto como números. En lugar de definir funciones separadas para cada tipo, los tipos genéricos te permiten crear una única función que trabaja con un tipo de marcador de posición (a menudo representado por letras como T, U o V). Este marcador de posición actúa como una plantilla, permitiendo que la función se adapte a diferentes tipos de datos cuando se usa:

function identidad<T>(valor: T): T {
  return valor;
}

const nombre = identidad<string>("Alice"); // Tipo: cadena de texto
const edad = identidad<number>(30); // Tipo: número
Enter fullscreen mode Exit fullscreen mode

Casos de Uso Comunes en el Desarrollo Frontend:

1. Componentes Reutilizables:

Crea componentes que pueden aceptar y trabajar con diferentes tipos de datos:

interface ElementoLista<T> {
  datos: T;
}

const elementosLista: ElementoLista<string | number>[] = [
  { datos: "Elemento 1" },
  { datos: 2 },
];

function renderizarLista<T>(elementos: ElementoLista<T>[]): string {
  let lista = "";
  for (const elemento of elementos) {
    lista += `<li>${elemento.datos}</li>`;
  }
  return lista;
}

// Ejemplo de uso:
const listaHTML = renderizarLista(elementosLista);
console.log(listaHTML);
Enter fullscreen mode Exit fullscreen mode

2. Interacciones Seguras con APIs:

Garantiza la seguridad de tipos al interactuar con APIs que devuelven diferentes estructuras de datos:

interface RespuestaAPI<T> {
  datos: T;
  estado: number;
}

async function obtenerDatos<T>(url: string): Promise<RespuestaAPI<T>> {
  // ... lógica de obtención
}

const datosUsuario = await obtenerDatos<Usuario>("https://api.ejemplo.com/usuarios/1");
// datosUsuario tiene tipo RespuestaAPI<Usuario>, garantizando la seguridad de tipos
Enter fullscreen mode Exit fullscreen mode

3. Funciones Utilitarias:

Crea funciones que operen en diferentes tipos de datos con una lógica consistente:

function mapearValores<T, U>(valores: T[], mapeador: (valor: T) => U): U[] {
  return valores.map(mapeador);
}

const cadenas = mapearValores(["manzana", "banana"], (cadena) => cadena.toUpperCase()); // cadenas: string[]
const numeros = mapearValores([1, 2, 3], (numero) => numero * 2); // numeros: number[]
Enter fullscreen mode Exit fullscreen mode

Técnicas Avanzadas con Tipos Genéricos

Más allá de los usos fundamentales, los tipos genéricos ofrecen funcionalidades avanzadas para mejorar aún más tu desarrollo frontend:

1. Tipos Condicionales:

Los tipos genéricos se pueden combinar con lógica condicional para crear tipos basados en condiciones de tiempo de ejecución. Esto permite un manejo dinámico de tipos dentro de la definición genérica:

type ArregloCadenasONumeros<T> = T extends string ? string[] : T extends number ? number[] : never;

const arregloCadenas: ArregloCadenasONumeros<string> = ["manzana", "banana"];
const arregloNumeros: ArregloCadenasONumeros<number> = [1, 2, 3];

// Esto daría como resultado un error de tipo:
// const arregloInvalido: ArregloCadenasONumeros<boolean> = [true, false];
Enter fullscreen mode Exit fullscreen mode

2. Tipos Mapeados:

Estos tipos involucran transformar las propiedades de un tipo existente en un tipo nuevo. Esto puede ser útil para crear funciones utilitarias o iterar sobre las propiedades de un objeto con seguridad de tipos:

interface Usuario {
  nombre: string;
  edad: number;
}

type UsuarioMayúsculas<T extends Usuario> = {
  [P in keyof T]: T[P] extends string ? Uppercase<T[P]> : T[P];
};

const usuarioMayúsculas: UsuarioMayúsculas<Usuario> = {
  nombre: "JUAN",
  edad: 30,
};
Enter fullscreen mode Exit fullscreen mode

3. Tipos Utilitarios Genéricos:

TypeScript proporciona tipos utilitarios genéricos incorporados como Partial<T>, Pick<T, K> y Readonly<T>que simplifican las manipulaciones comunes de tipos. Además, puedes crear tipos utilitarios personalizados para necesidades específicas:

type Nullable<T> = T | null; // Permite valores nulos para cualquier tipo

type Omitir<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; // Excluye propiedades específicas

const datosUsuarioOpcionales: Nullable<Usuario> = { nombre: "Alice" }; // Válido

const infoUsuario: Omitir<Usuario, "edad"> = { nombre: "Bob" }; // Válido (excluye "edad")
Enter fullscreen mode Exit fullscreen mode

Beneficios de Usar Tipos Genéricos:

  • Mayor Reutilización: Los componentes genéricos se pueden usar con varios tipos de datos, eliminando la necesidad de código repetitivo para cada tipo específico.
  • Seguridad de Tipos Mejorada: La verificación de tipos se aplica incluso cuando se utilizan tipos genéricos, lo que evita errores en tiempo de ejecución debido a tipos de datos incorrectos.
  • Estructura de Código Más Clara: Los tipos genéricos promueven un código mantenible al expresar explícitamente los tipos de datos esperados para componentes y funciones.

Conclusión

Los tipos genéricos te permiten crear componentes y funciones adaptables y seguras para tipos en tu desarrollo frontend. Al incorporarlos a tu flujo de trabajo, puedes impulsar la reutilización del código, la mantenibilidad y la calidad general de la aplicación. Aprovecha el poder de los genéricos para abordar diversos tipos de datos con confianza y construir experiencias frontend robustas.

Recursos

Top comments (0)