Genéricos en TypeScript

0
123
genéricos en typescript

Índice de contenidos

  1. Introducción
  2. ¿Qué son los genéricos?
  3. Restricciones de tipos en genéricos
  4. Interfaz y clase genérica
  5. Conclusión

Introducción

Los genéricos en TypeScript es un concepto algo avanzado y que puede resultar complejo al principio, mi objetivo con este tutorial es explicar algunos de sus conceptos para que el trabajo con genéricos sea más familiar.

¿Qué son los genéricos?

Podríamos decir, que los genéricos es como pasar los tipos por parámetro a funciones, clases o interfaces.

Si has trabajado con TypeScript ya sabrás que este nos permite crear un tipado estricto a nuestras interfaces, clases y funciones. Pero imagínate que queremos que alguno de estos componentes funcione con más de un tipo. Es posible que se te haya pasado por la cabeza usar el tipo any y sí, funcionaría, pero estaríamos perdiendo el gran potencial que nos aporta el sistema de comprobación de tipos de TypeScript.

Aquí es donde entran los genéricos, proporcionándonos mayor flexibilidad al trabajar con tipos y facilitándonos la reutilización de código.

Veamos un ejemplo para entenderlo mejor:

function getInvertedArray(arr: any[]): any[]{
	return arr.slice().reverse();
}

Tenemos esta función que devuelve una copia del array recibido y además invertido.

Al utilizar el tipo any no hay nada que nos impida mezclar tipos dentro del array, vamos a verlo:

let invertedNumberArray = getInvertedArray([1, 2, 3, 4, 5]);

invertedNumberArray.push('some string');

console.log(invertedNumberArray); // [5, 4, 3, 2, 1, 'some string']

Como podemos ver hemos añadido un string a un array de números.

Ahora vamos a volver a escribir el mismo código pero esta vez utilizando los genéricos:

function getInvertedArray<T>(arr: T[]):T[]{
	return arr.slice().reverse();
}
let invertedNumberArray = getInvertedArray<number>([1, 2, 3, 4, 5]);

invertedNumberArray.push('some string'); // Error

Ahora el parámetro T representa nuestra variable de tipo, en este caso le hemos dicho que T equivale a number , por lo que al intentar hacer un push de un string TypeScript nos avisará con un error.

Para que lo veas más claro la función quedaría tal que así:

function getInvertedArray(arr: number[]):number[]{
	return arr.slice().reverse();
}

También se pueden inferir los tipos sin necesidad de especificarlo mediante <number> . En siguiente ejemplo funcionaría igual:

let invertedNumberArray = getInvertedArray([1, 2, 3, 4, 5]);

invertedNumberArray.push('some string'); // Error

Sin embargo al añadirle el tipo de una manera más consciente reducimos el número de errores, por ejemplo este:

let invertedNumberArray = getInvertedArray<number>(['1', '2', '3', '4', '5']); // Error

Aquí estamos especificando que la función tiene que recibir un number[] . Y al pasarle un string[] nos daría error.

No estamos limitados a utilizar únicamente una variable de tipo, veamos un ejemplo:

function typeLogger<T, K>(param1: T, param2: K ) {
	console.log(param1, typeof param1);
	console.log(param2, typeof param2);
}

En esta función recibe 2 parámetros e imprime el valor de estos y su tipo:

typeLogger<number, string>(1, 'one');
// 1 number
// one string
typeLogger(1, 'one');
// 1 number
// one string
typeLogger<number, string>('1', 'one'); // Error

typeLogger<boolean, string>(true, 'true');
// true boolean
// true string
typeLogger(true, 'true');
// true boolean
// true string

Restricciones de tipos en genéricos

Por defecto, los genéricos aceptan cualquier tipo, pero nosotros podemos especificar los tipos que queremos que acepte el genérico mediante el operador extends . Veamos un ejemplo utilizando la función typeLogger que utilizamos antes:

type ValidTypes = string | number;

function typeLogger<T extends ValidTypes, K>(param1: T, param2: K ) {
	console.log(param1, typeof param1);
	console.log(param2, typeof param2);
}

Ahora K podrá ser cualquier tipo pero T únicamente podrá ser string o number

Interfaz y Clase Genérica

Como hemos mencionado también se pueden utilizar genéricos en la declaración de interfaces.

interface SomeInterface <T, K> {
	something: T
	otherThing: K
}

Y también en la declaración de clases:

class genericClass<T> {
    genericAttribute: T;

    constructor(genericAttribute: T) {
        this.genericAttribute = genericAttribute;
    }
}

let something = new genericClass<boolean>(true);

Y esta interfaz por ejemplo podría implementarse en una clase, por ejemplo:

class someClass<X, Y> implements SomeInterface<X, Y> {
    something: X;
    otherThing: Y;
    constructor(something: X, otherThing: Y) {
        this.something = something;
        this.otherThing = otherThing;
    }
}

let something = new someClass<number, string>(1, 'this is something');

Conclusión

En el articulo hemos comentado los aspectos básicos sobre los genéricos, pero con los genéricos se pueden hacer maravillas y pueden llegar a ser muy complejos. Espero que estas nociones básicas os ayuden a empezar a trabajar con ellos.

Gracias por tu atención 😊

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad