Está en la página 1de 6

15/10/2018 Generics | TypeScript Documentation | CODE Examples [Español]

TypeScript TypeScript
 Advanced Types
Referencias entre proyectos entre dos proyectos
 Basic Types
Llamar a una función al hacer clic en evento en Angular 2
 Classes

 Declaration Files

Generics  Merging

 Decorators

 Enums
Introducción  Functions

Una parte importante de la ingeniería de software es construir componentes que no solo tengan API consi  Generics
stentes y bien definidas, sino que también sean reutilizables. Los componentes que son capaces de trabaja Generics
r con los datos actuales y los datos del futuro le brindarán las capacidades más flexibles para crear sistema
s de software grandes. Classes

En idiomas como C # y Java, una de las herramientas principales en la caja de herramientas para crear com Constraints
ponentes reutilizables es genéricos , es decir, poder crear un componente que pueda funcionar en una vari
Types
edad de tipos en lugar de uno solo. Esto permite a los usuarios consumir estos componentes y usar sus pr
opios tipos. Using Class Types in Generics

 Interfaces
Hola mundo de genéricos  Iterators & Generators

 JSX
Para comenzar, hagamos el "mundo de saludo" de los genéricos: la función de identidad. La función de ide
ntidad es una función que devolverá todo lo que se transfiere. Puede pensar en esto de manera similar al c  Mixins
omando echo .
 Module Resolution
Sin los genéricos, tendríamos que darle a la función de identidad un tipo específico:
 Modules

function identity(arg: number): number {  Namespaces


return arg;
}  & Modules

 Project Configuration
O bien, podríamos describir la función de identidad utilizando any tipo:
 Symbols
function identity(arg: any): any {
 Triple-Slash Directives
return arg;
}  Tutorials

 Type Checking JavaScript Files


Si bien el uso de any es ciertamente genérico porque hará que la función acepte cualquiera y todos los ti
pos para el tipo de arg , en realidad estamos perdiendo la información sobre qué tipo era cuando la funci  Compatibility
ón retorna. Si aprobamos un número, la única información que tenemos es que se puede devolver cualqui
 Inference
er tipo.
 Variable Declarations
En cambio, necesitamos una forma de capturar el tipo de argumento de tal manera que también podamos
usarlo para denotar lo que se está devolviendo. Aquí, usaremos una variable de tipo , un tipo especial de v  What's New
ariable que funciona en tipos en lugar de valores.

function identity<T>(arg: T): T {


return arg;
}

Ahora hemos agregado una variable de tipo T a la función de identidad. Esta T nos permite capturar el ti
po que proporciona el usuario (por ejemplo, number ), para que podamos usar esa información más adela
nte. Aquí, utilizamos T nuevamente como el tipo de devolución. En la inspección, ahora podemos ver que
se usa el mismo tipo para el argumento y el tipo de devolución. Esto nos permite traficar esa información
de tipo en un lado de la función y en el otro.

https://code-examples.net/es/docs/typescript/handbook/generics 1/6
15/10/2018 Generics | TypeScript Documentation | CODE Examples [Español]

Decimos que esta versión de la función de identity es genérica, ya que funciona en un rango de tipos.
A diferencia del uso de any , también es igual de preciso (es decir, no pierde ninguna información) como l
a primera función de identity que utilizó números para el argumento y el tipo de devolución.

Una vez que hemos escrito la función de identidad genérica, podemos llamarla de una de dos maneras. La
primera forma es pasar todos los argumentos, incluido el argumento de tipo, a la función:

let output = identity<string>("myString"); // type of output will be 'string'

Aquí establecemos explícitamente que T sea una string como uno de los argumentos de la llamada de
función, denotada con <> alrededor de los argumentos en lugar de () .

La segunda forma también es quizás la más común. Aquí usamos la inferencia de argumento tipo , es decir,
queremos que el compilador establezca el valor de T para nosotros automáticamente en función del tipo
de argumento que pasemos:

let output = identity("myString"); // type of output will be 'string'

Observe que no tuvimos que pasar explícitamente el tipo en los corchetes angulares ( <> ); el compilador
solo miró el valor "myString" y estableció T en su tipo. Si bien la inferencia de argumento de tipo pued
e ser una herramienta útil para mantener el código más corto y legible, es posible que deba pasar explícita
mente los argumentos de tipo como lo hicimos en el ejemplo anterior cuando el compilador no puede infe
rir el tipo, como puede suceder en ejemplos más complejos .

Trabajando con variables genéricas de tipo


Cuando empiece a usar medicamentos genéricos, notará que cuando crea funciones genéricas como la id
entity , el compilador exigirá que utilice correctamente los parámetros tipeados genéricamente en el cue
rpo de la función. Es decir, que realmente trata estos parámetros como si pudieran ser de cualquier tipo.

Tomemos nuestra función de identity de antes:

function identity<T>(arg: T): T {


return arg;
}

¿Qué pasa si también queremos registrar la longitud del argumento arg en la consola con cada llamada?
Podríamos sentirnos tentados de escribir esto:

function loggingIdentity<T>(arg: T): T {


console.log(arg.length); // Error: T doesn't have .length
return arg;
}

Cuando lo hagamos, el compilador nos dará un error de que estamos usando el miembro .length de a
rg , pero en ninguna parte hemos dicho que arg tiene este miembro. Recuerde, dijimos anteriormente q
ue estas variables de tipo representan todos y cada uno de los tipos, por lo que alguien que use esta funci
ón podría haber pasado en un number , que no tiene un miembro .length .

Digamos que en realidad hemos pensado que esta función funcione en matrices de T lugar de T directa
mente. Como estamos trabajando con matrices, el miembro .length debería estar disponible. Podemos
describir esto como si creáramos arreglos de otros tipos:

function loggingIdentity<T>(arg: T[]): T[] {


console.log(arg.length); // Array has a .length, so no more error
return arg;
}

Puede leer el tipo de loggingIdentity como "la función genérica loggingIdentity toma un parámet
ro de tipo T , y un argumento arg que es una matriz de T s, y devuelve una matriz de T s." Si pasamos
en una matriz de números, Recuperaría una serie de números, ya que T uniría al number . Esto nos permi
te usar nuestra variable de tipo genérico T como parte de los tipos con los que estamos trabajando, en lu
gar de todo el tipo, lo que nos brinda una mayor flexibilidad.

https://code-examples.net/es/docs/typescript/handbook/generics 2/6
15/10/2018 Generics | TypeScript Documentation | CODE Examples [Español]

Alternativamente, podemos escribir el ejemplo de muestra de esta manera:

function loggingIdentity<T>(arg: Array<T>): Array<T> {


console.log(arg.length); // Array has a .length, so no more error
return arg;
}

Es posible que ya esté familiarizado con este estilo de tipo de otros idiomas. En la siguiente sección, explic
aremos cómo puede crear sus propios tipos genéricos, como Array<T> .

Tipos genéricos
En secciones anteriores, creamos funciones de identidad genéricas que funcionaban en un rango de tipos.
En esta sección, exploraremos el tipo de funciones en sí y cómo crear interfaces genéricas.

El tipo de funciones genéricas es similar a las funciones no genéricas, con los parámetros de tipo enumera
dos primero, de forma similar a las declaraciones de funciones:

function identity<T>(arg: T): T {


return arg;
}

let myIdentity: <T>(arg: T) => T = identity;

También podríamos haber usado un nombre diferente para el parámetro de tipo genérico en el tipo, siemp
re que el número de variables de tipo y la forma en que se usen las variables de tipo se alineen.

function identity<T>(arg: T): T {


return arg;
}

let myIdentity: <U>(arg: U) => U = identity;

También podemos escribir el tipo genérico como una firma de llamada de un tipo literal de objeto:

function identity<T>(arg: T): T {


return arg;
}

let myIdentity: {<T>(arg: T): T} = identity;

Lo que nos lleva a escribir nuestra primera interfaz genérica. Tomemos el objeto literal del ejemplo anterior
y lo movemos a una interfaz:

interface GenericIdentityFn {
<T>(arg: T): T;
}

function identity<T>(arg: T): T {


return arg;
}

let myIdentity: GenericIdentityFn = identity;

En un ejemplo similar, podemos querer mover el parámetro genérico para que sea un parámetro de toda l
a interfaz. Esto nos permite ver de qué tipo (s) somos genéricos (ej. Dictionary<string> lugar de solo
Dictionary ). Esto hace que el parámetro de tipo sea visible para todos los demás miembros de la interf
az.

https://code-examples.net/es/docs/typescript/handbook/generics 3/6
15/10/2018 Generics | TypeScript Documentation | CODE Examples [Español]

interface GenericIdentityFn<T> {
(arg: T): T;
}

function identity<T>(arg: T): T {


return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

Tenga en cuenta que nuestro ejemplo ha cambiado para ser algo ligeramente diferente. En lugar de descri
bir una función genérica, ahora tenemos una firma de función no genérica que es parte de un tipo genéric
o. Cuando usamos GenericIdentityFn , ahora también necesitaremos especificar el argumento de tipo
correspondiente (aquí: number ), bloqueando efectivamente lo que usará la firma de llamada subyacente.
Comprender cuándo colocar el parámetro de tipo directamente en la firma de la llamada y cuándo colocarl
o en la interfaz en sí será útil para describir qué aspectos de un tipo son genéricos.

Además de las interfaces genéricas, también podemos crear clases genéricas. Tenga en cuenta que no es p
osible crear enumeraciones genéricas y espacios de nombres.

Clases genéricas
Una clase genérica tiene una forma similar a una interfaz genérica. Las clases genéricas tienen una lista de
parámetros de tipo genérico entre corchetes angulares ( <> ) siguiendo el nombre de la clase.

class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();


myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

Este es un uso bastante literal de la clase GenericNumber , pero es posible que haya notado que nada lo
restringe para usar solo el tipo de number . Podríamos haber usado string o incluso objetos más compl
ejos.

let stringNumeric = new GenericNumber<string>();


stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

alert(stringNumeric.add(stringNumeric.zeroValue, "test"));

Al igual que con la interfaz, poner el parámetro tipo en la clase en sí nos permite asegurarnos de que toda
s las propiedades de la clase funcionen con el mismo tipo.

Como cubrimos en nuestra sección de clases , una clase tiene dos lados en su tipo: el lado estático y el lad
o de la instancia. Las clases genéricas solo son genéricas por su lado de instancia en lugar de su lado estáti
co, por lo que cuando se trabaja con clases, los miembros no pueden usar el parámetro de tipo de la clase.

Restricciones genéricas
Si recuerda un ejemplo anterior, a veces puede querer escribir una función genérica que funcione en un co
njunto de tipos en los que tenga algún conocimiento sobre las capacidades que tendrá ese conjunto de tip
os. En nuestro ejemplo loggingIdentity , queríamos poder acceder a la propiedad .length de arg ,
pero el compilador no pudo probar que cada tipo tenía una propiedad .length , por lo que nos advierte
que no podemos hacer esta suposición.

function loggingIdentity<T>(arg: T): T {


console.log(arg.length); // Error: T doesn't have .length
return arg;
}

https://code-examples.net/es/docs/typescript/handbook/generics 4/6
15/10/2018 Generics | TypeScript Documentation | CODE Examples [Español]

En lugar de trabajar con todos los tipos, nos gustaría restringir esta función para que funcione con todos lo
s tipos que también tengan la propiedad .length . Siempre que el tipo tenga este miembro, lo permitire
mos, pero se requiere tener al menos este miembro. Para hacerlo, debemos enumerar nuestro requisito co
mo una restricción sobre lo que T puede ser.

Para hacerlo, crearemos una interfaz que describa nuestra restricción. Aquí, crearemos una interfaz que tie
ne una única propiedad .length y luego usaremos esta interfaz y la palabra clave extends para denota
r nuestra restricción:

interface Lengthwise {
length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {


console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}

Debido a que la función genérica ahora está restringida, ya no funcionará en ninguno y todos los tipos:

loggingIdentity(3); // Error, number doesn't have a .length property

En cambio, necesitamos pasar valores cuyo tipo tenga todas las propiedades requeridas:

loggingIdentity({length: 10, value: 3});

Uso de parámetros de tipo en restricciones genérica


s
Puede declarar un parámetro de tipo que esté restringido por otro parámetro de tipo. Por ejemplo, aquí n
os gustaría obtener una propiedad de un objeto dado su nombre. Nos gustaría asegurarnos de que no est
amos agarrando accidentalmente una propiedad que no existe en el obj , por lo que colocaremos una res
tricción entre los dos tipos:

function getProperty<T, K extends keyof T>(obj: T, key: K) {


return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a"); // okay


getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.

Uso de tipos de clase en genéricos


Al crear fábricas en TypeScript utilizando genéricos, es necesario hacer referencia a los tipos de clase por s
us funciones de constructor. Por ejemplo,

function create<T>(c: {new(): T; }): T {


return new c();
}

Un ejemplo más avanzado utiliza la propiedad prototipo para inferir y restringir las relaciones entre la funci
ón constructora y el lado de instancia de los tipos de clase.

https://code-examples.net/es/docs/typescript/handbook/generics 5/6
15/10/2018 Generics | TypeScript Documentation | CODE Examples [Español]

class BeeKeeper {
hasMask: boolean;
}

class ZooKeeper {
nametag: string;
}

class Animal {
numLegs: number;
}

class Bee extends Animal {


keeper: BeeKeeper;
}

class Lion extends Animal {


keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {


return new c();
}

createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!

 Español  Top

https://code-examples.net/es/docs/typescript/handbook/generics 6/6

También podría gustarte