Cómo usar decoradores en TypeScript
Esta combinación permite a los desarrolladores utilizar el ecosistema completo de JavaScript y las características del lenguaje y, al mismo tiempo, pueden agregar verificación de tipos estáticos, enumeraciones, clases e interfaces opcionales. Una de estas características adicionales es la compatibilidad con decoradores.
El decorador es un método para decorar miembros de una clase o la clase misma. Tiene funciones adicionales.
Cuando aplicamos un decorador a una clase o miembro de una clase, en realidad estamos llamando a una función que recibirá los detalles del contenido que se está decorando. La implementación del decorador podrá transformar dinámicamente el código, agregar funcionalidad adicional y reducir el código repetitivo.
Son una forma de metaprogramación en TypeScript, una técnica de programación que permite a los programadores crear código que utiliza otro código en la propia aplicación como datos.
Este tutorial compartirá cómo crear sus propios decoradores para clases y miembros de la clase en TypeScript, y cómo usarlos.
Nos guiará a través de diferentes ejemplos de código que podemos seguir en nuestro propio entorno TypeScript o en TypeScript Playground, un entorno online que nos permite escribir TypeScript directamente en el navegador.
Preparación / A punto de comenzar a trabajar
Para completar este ejemplo de tutorial, debemos realizar los siguientes preparativos:
Habilitar la compatibilidad con decoradores en TypeScript.
Actualmente, los decoradores todavía son una característica experimental en TypeScript, por lo que primero deben habilitarse. En esta sección, aprenderemos cómo habilitar decoradores en TypeScript, dependiendo de cómo use TypeScript.
CLI del compilador TypeScript
Para habilitar la compatibilidad con decoradores cuando se utiliza la CLI del compilador TypeScript (tsc), el único paso adicional necesario es pasar un indicador adicional: experimentalDecorators: p>
tsconfig.json
Cuando trabajamos en un proyecto que utiliza un archivo tsconfig.json, para habilitar el decorador experimental debemos agregar el atributo del decorador experimental al objeto compilerOptions:
En TypeScript Playground, los decoradores están habilitados de forma predeterminada.
Uso de la sintaxis de decoración
En esta sección, aplicaremos decoradores en clases de TypeScript.
En TypeScript, podemos crear un decorador usando la sintaxis especial @expression, donde expresión es una función que se llamará automáticamente en tiempo de ejecución y que contiene detalles sobre el objetivo del decorador.
El propósito de un decorador depende de dónde los agreguemos. Actualmente, se pueden agregar decoradores a los siguientes componentes de una clase:
Por ejemplo, digamos que tenemos un decorador llamado seal que llama a Object.seal en la clase. Para usar nuestro decorador, podemos escribir el siguiente código:
Lo mismo se aplica a todos los demás tipos de decoradores:
Para agregar varios decoradores, agréguelos uno tras otro:
Crear un decorador de clases en TypeScript
En esta sección, veremos los pasos para crear un decorador de clases en TypeScript.
Para el decorador llamado @decoratorA, le decimos a TypeScript que debe llamar a la función decoradorA. Se llamará a la función decoradorA, que contiene detalles sobre cómo usar el decorador en su código.
Para crear nuestro propio decorador, debemos crear una función con el mismo nombre que el decorador.
Es decir, para crear el decorador de clase sellada que vio en la sección anterior, debe crear una función sellada que reciba un conjunto específico de parámetros. Hagamos esto:
Los parámetros pasados al decorador dependerán de dónde se utilice el decorador. El primer parámetro suele denominarse objetivo.
Luego, en la función de sellado, llame a Object.seal en el objetivo (es decir, el constructor de la clase) y su prototipo. Al hacerlo, no se pueden agregar nuevas propiedades al constructor de clases o sus propiedades, y las propiedades existentes se marcarán como no configurables.
Es importante recordar que actualmente no es posible ampliar el tipo TypeScript de destino cuando se utilizan decoradores. Esto significa, por ejemplo, que no se pueden utilizar decoradores para agregar nuevos campos a una clase y convertirlos en tipos seguros.
Si un valor se devuelve en un decorador de clase sellado, se convierte en el nuevo constructor de la clase. Esto es útil si desea anular completamente el constructor de clases.
El primer decorador ha sido creado y utilizado en una clase.
A continuación, aprenderemos a crear una fábrica de decoración.
Crear una fábrica de decoración
A veces, al aplicar un decorador, necesitamos pasarle opciones adicionales. Para ello tenemos que utilizar fábrica de decoración.
Aquí aprenderemos a crear y utilizar estas fábricas.
Una fábrica de decoración es una función que devuelve otra función. Reciben este nombre porque no son implementaciones de decoradores en sí mismas.
En su lugar, devuelven otra función que se encarga de implementar el decorador y actúa como función contenedora. Son útiles para hacer que un decorador sea personalizable al permitir que el código del cliente pase opciones al decorador cuando lo usa.
Supongamos que hay una clase de decorador llamada decorador ora. Queremos agregar una opción que se pueda configurar al llamar al decorador, como un indicador booleano. Esto se puede lograr escribiendo una fábrica de decoradores similar a la siguiente. :
p>Aquí, la función decoradorA devuelve otra función con una implementación decoradora. Observe cómo la fábrica de decoración recibe una bandera booleana como único parámetro:
Cuando usamos un decorador, podemos pasar el valor de este parámetro.
Vea el código resaltado en el siguiente ejemplo:
Aquí, cuando usamos decoradorUn decorador, llamaremos a la fábrica de decoradores y estableceremos algún parámetro BooleanFlag en verdadero.
Se ejecutará la implementación del decorador. Esto nos permite cambiar el comportamiento del decorador según cómo se utiliza, lo que hace que nuestros decoradores sean fáciles de personalizar y reutilizar en nuestras aplicaciones.
Tenga en cuenta que debemos pasar todos los parámetros esperados por la fábrica de decoración. Si simplemente aplicamos el decorador sin pasar ningún parámetro, como en el siguiente ejemplo:
El compilador de TypeScript dará dos errores, que pueden variar según el tipo de decorador. Para los decoradores de clase, los errores son 1238 y 1240:
Acabamos de crear una fábrica de decoración que puede recibir parámetros y cambiar su comportamiento en función de esos parámetros.
A continuación, aprenderemos a crear un decorador de propiedades.
Crear decoradores de propiedades
Las propiedades de clase son otro lugar donde se pueden usar decoradores, y aquí aprenderemos cómo crearlos.
Cualquier decorador de propiedad recibirá los siguientes parámetros:
Actualmente no hay forma de obtener el descriptor de propiedad como parámetro. Esto se debe a que el decorador de propiedades se inicializó en TypeScript.
Esta es una función decorada que imprime el nombre del miembro en la consola:
Cuando ejecutamos el código TypeScript anterior, verá lo siguiente en la consola:
Podemos utilizar decoradores de propiedades para anular las propiedades decoradas. Esto se puede hacer usando Object.defineProperty con un nuevo definidor y captador para la propiedad.
Veamos cómo crear un decorador llamado lista de permitidos que solo permita que las propiedades se establezcan en valores que están presentes en la lista de permitidos estática:
Primero, agregaremos lo siguiente en la parte superior del código Cree una lista de permisos estática:
Luego, creamos un decorador de propiedades:
Observe cómo usamos cualquiera como tipo de destino:
Para el decorador de propiedades, el objetivo El tipo de parámetro puede ser el constructor de la clase o el prototipo de la clase, en cuyo caso es más fácil de usar cualquiera de los dos.
En la primera línea de la implementación del decorador, almacenamos el valor actual de la propiedad decorada en la variable currentValue:
Para propiedades estáticas, esto se establecerá en su valor predeterminado ( si lo hubiera).
Para propiedades no estáticas, esto siempre será indefinido. Esto se debe a que en tiempo de ejecución, en el código JavaScript compilado, los decoradores se ejecutan antes de que las propiedades de la instancia se establezcan en sus valores predeterminados.
Luego anularemos la propiedad usando Object.defineProperty:
La llamada a Object.defineProperty tiene un captador y un definidor. El captador devuelve el valor almacenado en la variable currentValue.
Si currentVariable está en la lista permitida, el configurador establecerá su valor en newValue.
Usemos el decorador que acabas de escribir. Cree la siguiente clasificación de persona:
Ahora crearemos una nueva instancia de esta clase, probaremos la configuración y obtendremos la propiedad de instancia Nombre:
Cuando ejecutemos el código, deberíamos ver el siguiente resultado:
El valor nunca se establecerá en Peter porque Peter no está en la lista de permitidos.
¿Qué pasa si queremos que el código sea más reutilizable y permitir que se establezcan listas de permisos cuando se aplican decoradores? Este es un gran caso de uso para decorar fábricas.
Hagamos esto convirtiendo el decorador enablelistOnly en una fábrica de decoradores.
Aquí, envolvemos la implementación anterior en otra función, la fábrica de decoración. La fábrica de decoración recibe un parámetro llamado Lista de permitidos, que es una matriz de cadenas.
Ahora, para usar el decorador, tenemos que pasar una lista de licencias como se muestra en el código resaltado a continuación:
Intentando ejecutar un código similar al escrito antes, pero con Nuevos cambios:
El resultado se ve así:
Esto muestra que funciona como se esperaba, person.name nunca se establece en Peter porque Peter no está en la lista blanca dada.
Ahora que hemos creado nuestro primer decorador de propiedades utilizando una función de decorador público y una fábrica de decoradores, es hora de ver cómo crear decoradores para descriptores de acceso de clase.
Creación de decoradores de acceso
Aquí aprenderemos cómo decorar accesorios de clase.
Al igual que el decorador de propiedades, el decorador utilizado en el descriptor de acceso recibe los siguientes parámetros:
Pero a diferencia del decorador de propiedades, también recibe el tercer parámetro, el descriptor de acceso. El descriptor de propiedad del miembro. .
Debido a que un descriptor de propiedad contiene definidores y captadores para un miembro específico, los modificadores de acceso solo se pueden aplicar al definidor o captador de un solo miembro, no a ambos.
Si devolvemos un valor del decorador de acceso, se convierte en el nuevo descriptor de propiedad para el acceso de los miembros getter y setter.
Aquí hay un ejemplo de un decorador que se puede usar para cambiar el indicador enumerable de un descriptor de acceso getter/setter:
Observe cómo usamos una fábrica de decoración en el ejemplo. Esto nos permite especificar indicadores enumerables al llamar al decorador.
A continuación se explica cómo utilizar los decoradores:
Los decoradores de accesorios son similares a los decoradores de propiedades. La única diferencia es que reciben un tercer parámetro con un descriptor de propiedad. Ahora hemos creado nuestro primer decorador de acceso.
A continuación, aprenderemos cómo crear un decorador de métodos.
Crear decoradores de métodos
Aquí aprenderemos a utilizar decoradores de métodos.
Los decoradores de métodos se implementan de forma muy similar a como se crean los decoradores de acceso. Los parámetros pasados a la implementación del decorador son los mismos que los pasados al decorador de acceso.
Reutilicemos el mismo decorador enumerable que creamos anteriormente, pero esta vez en el método getFullName de la clase Persona a continuación:
Si devolvemos un valor, que se convertirá en la nueva propiedad del método descriptor.
Creemos un decorador obsoleto que imprima el mensaje pasado en la consola cuando se usa el método y registre un mensaje indicando que el método ha quedado obsoleto:
Aquí usamos el Fábrica de decoración para crear un decorador. Esta fábrica de decoradores acepta un parámetro de tipo cadena, que está en desuso por una razón, como se muestra en la sección resaltada a continuación:
DeprecationReason registrará un mensaje en desuso en la consola en el futuro. En implementaciones donde los decoradores están en desuso, devolvemos un valor. Cuando devolvemos un valor de un decorador de métodos, ese valor anula el descriptor de propiedad del miembro.
Usamos esto para agregar un captador al método decorador. De esta manera, podemos cambiar la implementación del método en sí.
Pero ¿por qué no usar Object.defineProperty en lugar de devolver un nuevo decorador de propiedades para el método? Esto es necesario porque necesitamos acceder al valor de esta instancia de clase que está vinculada al método de clase no estático.
Si usamos Object.defineProperty directamente, no podremos recuperar su valor, y si este método lo usa de alguna manera, el decorador romperá nuestro código al ejecutar el método envuelto desde la implementación del decorador. .
En este caso, el valor this del captador está vinculado a la instancia de clase del método no estático y al constructor de clase del método estático.
Luego, cree una función contenedora local llamada wrapperFn en su captador que registre mensajes en la consola usando console.warn, pase el deprecationReason recibido de la fábrica de decoradores y luego lo llame usando el método propertyDescriptor.value Original.
Apply(this, args), que llama al método original de esta manera y vincula correctamente su valor this a la instancia de clase en caso de que sea un método no estático.
Luego usaremos defineProperty para anular el valor del método en la clase. Esto actúa como un mecanismo de memoria, ya que varias llamadas al mismo método ya no llamarán al captador, sino que llamarán directamente a wrapperFn.
Ahora usamos Object.defineProperty para establecer miembros en la clase, usando wrapperFn como su valor.
Usemos el decorador obsoleto:
Aquí, creamos una TestClass con dos propiedades: una estática y otra no estática. También creamos dos métodos: uno estático y otro no estático.
Luego aplicamos el decorador obsoleto a ambos métodos. Cuando ejecute el código, aparecerá lo siguiente en la consola:
Esto indica que ambos métodos están correctamente empaquetados por la función contenedora, que registra un mensaje en la consola y explica por qué está obsoleto.
Ya has creado tu primer decorador de métodos usando TypeScript.
A continuación, aprenderemos cómo crear el último tipo de decorador admitido por TypeScript, el decorador de parámetros.
Crear decoradores de parámetros
Los modificadores de parámetros se pueden utilizar en parámetros de métodos de clase.
Aquí aprenderemos cómo crear una función decorada para tomar parámetros.
Recibe los siguientes parámetros:
El índice del parámetro en la lista de parámetros del método.
No se puede cambiar nada relacionado con el parámetro en sí, por lo que este decorador solo es útil para observar el parámetro en sí (a menos que se use algo más avanzado, como metadatos de reflexión).
Este es un ejemplo de un decorador que imprime el índice y el nombre del método del parámetro decorado:
Luego puedes usar el decorador de parámetros de esta manera:
Al ejecutar el código anterior debería mostrarse lo siguiente en la consola:
Ahora hemos creado y ejecutado un decorador de parámetros e imprimimos el resultado que devuelve el índice del parámetro decorado.
Resumen
En este tutorial, implementamos todos los decoradores admitidos por TypeScript, los usamos con clases y aprendimos sobre sus diferencias.
Ahora puedes comenzar a escribir tus propios decoradores para reducir el código repetitivo en tu base de código, o usar un decorador con una biblioteca (como Mobx) con más confianza.
Eso es todo lo que tengo para compartir contigo. Si lo encuentras útil, recuerda compartirlo con tus amigos, puede serles útil.