Genéricos en TypeScript

Genéricos en TypeScript

Este artículo explica los genéricos en TypeScript.

YouTube Video

Genéricos en TypeScript

Los genéricos en TypeScript son una característica que permite definir funciones, clases e interfaces reutilizables y seguras en cuanto a tipos mediante la parametrización de tipos. Usar genéricos te permite escribir código que no depende de tipos específicos, lo que permite realizar las mismas operaciones en varios tipos.

Fundamentos de los Genéricos

Los genéricos actúan como plantillas que aceptan tipos como argumentos, permitiendo que las funciones y clases gestionen diferentes tipos.

Funciones Genéricas

El siguiente es un ejemplo de una función con sus tipos de argumentos especificados usando genéricos.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity<number>(42));       // 42
6console.log(identity<string>("Hello"));  // Hello
  • T es un argumento de tipo genérico que representa los tipos de los argumentos y el valor de retorno de la función. El tipo real se determina cuando se llama a la función.
  • Al especificar explícitamente <number> o <string>, estás definiendo el tipo.

Los tipos genéricos funcionan sin una especificación explícita porque TypeScript realiza inferencia de tipos.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity(42));       // 42
6console.log(identity("Hello"));  // Hello

Restricciones en los Genéricos

Al aplicar restricciones a los genéricos, puedes limitar que acepten solo tipos específicos.

1function loggingIdentity<T extends { length: number }>(arg: T): T {
2    console.log(arg.length);
3    return arg;
4}
5
6loggingIdentity("Hello");  // 5
7loggingIdentity([1, 2, 3]);  // 3
8
9// loggingIdentity(42);  // Error: number does not have a length property.
  • Especificar T extends { length: number } indica que T debe ser un tipo con una propiedad length. Por lo tanto, los tipos sin una propiedad length no serán aceptados.

Clases Genéricas

Las clases también pueden definirse usando genéricos. Las clases genéricas ofrecen tipos flexibles para propiedades y métodos.

 1class Box<T> {
 2    private _value: T;
 3
 4    constructor(value: T) {
 5        this._value = value;
 6    }
 7
 8    public getValue(): T {
 9        return this._value;
10    }
11
12    public setValue(value: T): void {
13        this._value = value;
14    }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue());  // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue());  // Hello
  • Box<T> declara el tipo T utilizado dentro de la clase como un genérico. Esto permite reutilizar la misma clase para diferentes tipos.

Interfaces Genéricas

Los genéricos también se pueden usar con interfaces.

 1interface Pair<T, U> {
 2    first: T;
 3    second: U;
 4}
 5
 6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
 7console.log(numberStringPair);  // { first: 1, second: 'One' }
 8
 9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair);  // { first: true, second: [ 1, 2, 3 ] }
  • Al especificar dos tipos genéricos con Pair<T, U>, puedes definir un objeto con una combinación de tipos diferentes.

Argumentos de Tipo Predeterminados

También es posible especificar un tipo predeterminado para los argumentos de tipo genéricos.

1function createArray<T = string>(length: number, value: T): T[] {
2    return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a"));   // ['a', 'a', 'a']
6console.log(createArray(3, 100));   // [100, 100, 100]
  • Estamos configurando el argumento de tipo predeterminado como string con <T = string>. Si no se especifica un tipo explícitamente, T será del tipo string.

Alias de Tipos Genéricos

Los genéricos también se pueden usar como alias de tipo (type).

 1type Result<T> = {
 2    success: boolean;
 3    data: T;
 4};
 5
 6const successResult: Result<number> = { success: true, data: 42 };
 7const errorResult: Result<string> = { success: false, data: "Error occurred" };
 8
 9console.log(successResult);  // { success: true, data: 42 }
10console.log(errorResult);    // { success: false, data: 'Error occurred' }
  • Result<T> representa un objeto de resultado que contiene datos del tipo T. De esta manera, puedes crear alias de tipo flexibles utilizando genéricos.

Múltiples Tipos Genéricos

Al usar múltiples tipos genéricos, puedes definir funciones y clases aún más versátiles.

1function merge<T, U>(obj1: T, obj2: U): T & U {
2    return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged);  // { name: 'Alice', title: 'Engineer' }
  • La función merge toma dos tipos diferentes T y U y los combina para devolver un nuevo objeto.

Resumen

  • Los genéricos permiten escribir código reutilizable y seguro al tratar los tipos como parámetros.
  • Al utilizar genéricos en funciones, clases e interfaces, puedes escribir una lógica flexible que maneje varios tipos.
  • Al añadir restricciones a los argumentos de tipo o configurar argumentos de tipo predeterminados, puedes controlar el alcance de los genéricos.

Al usar genéricos, puedes escribir código independiente del tipo y de propósito general, aprovechando al máximo el poderoso sistema de tipos de TypeScript.

Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.

YouTube Video