TypeScript para principiantes, Parte 5 Genéricos

El segundo tutorial de nuestra serie TypeScript para principiantes se centró en los tipos de datos básicos disponibles en TypeScript. La verificación de tipos en TypeScript nos permite asegurarnos de que las variables en nuestro código solo pueden tener tipos específicos de valores asignados a ellas. De esta manera, podemos evitar muchos errores al escribir código porque el IDE podrá decirnos cuándo estamos realizando una operación en un tipo que no es compatible. Esto hace que la comprobación de tipos sea una de las mejores características de TypeScript..

En este tutorial, nos centraremos en otra característica importante de este lenguaje genérico. Con los genéricos, TypeScript le permite escribir código que puede actuar en una variedad de tipos de datos en lugar de limitarse a uno solo. Aprenderá sobre la necesidad de genéricos en detalle y cómo es mejor que simplemente usar alguna Tipo de datos disponible en TypeScript.

La necesidad de genéricos

Si no está familiarizado con los genéricos, puede que se esté preguntando por qué los necesitamos en absoluto. En esta sección, responderé esta pregunta por ti. Comencemos escribiendo una función que devolverá un elemento aleatorio de una matriz de números.

function randomIntElem (theArray: number []): number let randomIndex = Math.floor (Math.random () * theArray.length); devuelve theArray [randomIndex];  dejar posiciones: número [] = [103, 458, 472, 458]; vamos randomPosition: número = randomIntElem (posiciones);

los randomElem La función que acabamos de definir toma una matriz de números como su único parámetro. El tipo de retorno de la función también se ha especificado como un número. Estamos usando el Math.random () función para devolver un número aleatorio de punto flotante entre 0 y 1. Multiplicándolo con la longitud de una matriz dada y llamando Math.floor () En el resultado nos da un índice aleatorio. Una vez que tenemos el índice aleatorio, devolvemos el elemento a ese índice específico.

Algún tiempo después, digamos que necesita obtener un elemento de cadena aleatorio de una matriz de cadenas. En este punto, puede decidir crear otra función que se dirija específicamente a las cadenas.

function randomStrElem (theArray: string []): string let randomIndex = Math.floor (Math.random () * theArray.length); devuelve theArray [randomIndex];  let colors: string [] = ['violet', 'indigo', 'blue', 'green']; let randomColor: string = randomStrElem (colors);

¿Qué sucede si necesita seleccionar un elemento aleatorio de una matriz de una interfaz que definió? No es factible crear una nueva función cada vez que desee obtener un elemento aleatorio de una matriz de diferentes tipos de objetos.

Una solución para este problema es establecer el tipo de parámetro de matriz que se pasa a las funciones como alguna[]. De esta manera, solo puede escribir su función una vez, y funcionará con una matriz de todos los tipos.

function randomElem (theArray: any []): any let randomIndex = Math.floor (Math.random () * theArray.length); devuelve theArray [randomIndex];  dejar posiciones = [103, 458, 472, 458]; vamos randomPosition = randomElem (posiciones); let colors = ['violet', 'indigo', 'blue', 'green']; vamos randomColor = randomElem (colores);

Como puede ver, podemos usar la función anterior para obtener posiciones aleatorias y colores aleatorios. Un problema importante con esta solución es que perderá la información sobre el tipo de valor que se devuelve. 

Antes, estábamos seguros de que posición aleatoria sería un número y Color aleatorio sería una cadena Esto nos ayudó a usar estos valores en consecuencia. Ahora, todo lo que sabemos es que el elemento devuelto podría ser de cualquier tipo. En el código anterior, podríamos especificar el tipo de Color aleatorio ser un número y todavia no recibo ningun error.

// Este código se compilará sin error. deja los colores: cuerda [] = ['violeta', 'índigo', 'azul', 'verde']; vamos randomColor: número = randomElem (colores);

Una mejor solución para evitar la duplicación de código mientras se conserva la información de tipo es usar genéricos. Aquí hay una función genérica que devuelve elementos aleatorios de una matriz.

función randomElem(theArray: T []): T let randomIndex = Math.floor (Math.random () * theArray.length); devuelve theArray [randomIndex];  let colors: string [] = ['violet', 'indigo', 'blue', 'green']; vamos randomColor: string = randomElem (colors);

Ahora, obtendré un error si intento cambiar el tipo de Color aleatorio desde cuerda a número. Esto demuestra que usar genéricos es mucho más seguro que usar alguna escribe en tales situaciones.

Uso de genéricos puede parecer muy limitante

La sección anterior discutió cómo se pueden usar los genéricos en lugar de alguna Escriba para escribir una sola función y evite sacrificar los beneficios de la verificación de tipos. Un problema con la función genérica que hemos escrito en la sección anterior es que TypeScript no nos permite realizar muchas operaciones en las variables que se le pasan.. 

Esto se debe a que TypeScript no puede hacer suposiciones sobre el tipo de variable que se pasará a nuestra función genérica de antemano. Como resultado, nuestra función genérica solo puede usar aquellas operaciones que son aplicables en todos los tipos de datos. El siguiente ejemplo debería aclarar este concepto..

función removeChar (theString: string, theChar: string): string deja theRegex = new RegExp (theChar, "gi"); devuelve theString.replace (theRegex, ");

La función anterior eliminará todas las apariciones del carácter especificado de la cadena dada. Es posible que desee crear una versión genérica de esta función para que también pueda eliminar dígitos específicos de un número dado, así como caracteres de una cadena. Aquí está la función genérica correspondiente.

función removeIt(theInput: T, theIt: string): T let theRegex = new RegExp (theIt, "gi"); devuelve theInput.replace (theRegex, ");

los eliminarChar La función no te mostró un error. Sin embargo, si usas reemplazar dentro eliminarIt,TypeScript te dirá que reemplazar no existe para el tipo 'T'. Esto se debe a que TypeScript ya no puede asumir que la entrada va a ser una cadena.

Esta restricción en el uso de diferentes métodos en una función genérica podría llevarlo a pensar que el concepto de genéricos no va a ser de mucha utilidad después de todo. Realmente no hay mucho que puedas hacer con un puñado de métodos que deben ser aplicables en todos los tipos de datos para que los uses dentro de una función genérica..

Una cosa importante que debe recordar en este punto es que generalmente no necesita crear funciones que se usarán con todo tipo de datos. Es más común crear una función que se utilizará con un conjunto específico o un rango de tipos de datos. Esta restricción en los tipos de datos hace que las funciones genéricas sean mucho más útiles..

Crear funciones genéricas usando restricciones

El genérico eliminarIt función de la sección anterior mostró un error porque la reemplazar El método interno debe utilizarse con cadenas, mientras que los parámetros que se le pasan podrían tener cualquier tipo de datos.. 

Puedes usar el se extiende palabra clave para restringir los tipos de datos que se pasan a una función genérica en TypeScript. sin embargo, se extiende Se limita a solo interfaces y clases. Esto significa que la mayoría de las funciones genéricas que cree tendrán parámetros que extienden una interfaz o clase base. 

Aquí hay una función genérica que imprime el nombre de las personas, miembros de la familia o celebridades que se le pasaron..

interfaz Personas nombre: cadena interfaz Familia nombre: cadena, edad: número, relación: cadena interfaz Celebrity extiende Personas profesión: cadena función nombre de impresión(theInput: T): void console.log ('Mi nombre es $ theInput.name');  sea serena: Celebrity = name: 'Serena Williams', profesión: 'Tennis Player' printName (serena);

En el ejemplo anterior, hemos definido tres interfaces, y cada una de ellas tiene una nombre propiedad. El genérico printName La función que hemos creado aceptará cualquier objeto que se extienda. Gente. En otras palabras, puede pasar un objeto familiar o famoso a esta función, e imprimirá su nombre sin ninguna queja. Puede definir muchas más interfaces, y siempre que tengan una nombre propiedad, podrá utilizar el printName funciona sin ningún problema.

Este fue un ejemplo muy básico, pero puede crear funciones genéricas más útiles una vez que se sienta más cómodo con todo el proceso. Por ejemplo, puede crear una función genérica que calcula el valor total de los diferentes artículos vendidos en un mes determinado, siempre que cada artículo tenga una precio propiedad para almacenar el precio y una vendido Propiedad que almacena el número de artículos vendidos. Con los genéricos, podrá usar la misma función siempre que los elementos amplíen la misma interfaz o clase..

Pensamientos finales

En este tutorial, he tratado de cubrir los conceptos básicos de los genéricos en TypeScript de una manera amigable para los principiantes. Comenzamos el artículo discutiendo la necesidad de los genéricos. Después de eso, aprendimos la forma correcta de usar los genéricos para evitar la duplicación de código sin sacrificar la capacidad de verificación de tipo. Una vez que entienda los conceptos básicos que se tratan aquí, puede leer más sobre genéricos en la documentación oficial..

Si tiene preguntas relacionadas con este tutorial, estaré encantado de responderlas en los comentarios..