¡Hasta -30% en todos los libros en línea,
eformaciones y vídeos*! Código: NEURONA30 Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí

Decoradores

Introducción

Un decorador permite agregar un comportamiento a un elemento durante la ejecución del código. Los decoradores toman la forma de funciones que posteriormente se pueden ejecutar mediante una expresión. Esta comienza con el carácter @, seguido del nombre del decorador que se va a ejecutar.

Sintaxis:

@DecoratorName 

No existe una convención de nomenclatura establecida para los decoradores. Sin embargo, la más utilizada es «PascalCase». En este capítulo, esta es la convención que se utilizará en los ejemplos.

Los decoradores se suelen utilizar para aplicar aspectos técnicos a los objetos, como, por ejemplo:

  • registrar la ejecución de un método,

  • inyectar dependencias,

  • notificar el cambio en el valor de una propiedad.

La otra ventaja de los decoradores es que permiten agregar metadatos. Estos especifican información adicional sobre un elemento. También ofrecen a los desarrolladores la posibilidad de escribir código de forma más declarativa. Este uso de decoradores es muy común y existen muchos frameworks y bibliotecas que los implementan para diferentes necesidades, como, por ejemplo:

  • definir el verbo HTTP vinculado a una acción de un controlador (framework/biblioteca de tipo Web MVC),

  • definir la estructura de una tabla en una base de datos (framework/biblioteca de tipo Object-Relational Mapping o mapeo relacional de objetos en español), ...

Decoradores experimentales

Para utilizar decoradores experimentales, es necesario habilitar opciones de compilación dedicadas al inicializar el archivo tsconfig.json. Al usar el comando tsc --init, se debe agregar los argumentos --experimentalDecorators (que permite activar el uso de decoradores en TypeScript) y --emitDecoratorMetadata (que permite inyectar en el código, durante la compilación, metadatos de tipos. Consulte la sección Metadatos) para habilitar el soporte completo para decoradores en TypeScript.

Ejemplo:

tsc --init --experimentalDecorators --emitDecoratorMetadata 

Ambas opciones de compilación tienen el mismo nombre que los argumentos del archivo tsconfig.json.

Ejemplo:

"experimentalDecorators": true,  
"emitDecoratorMetadata": true 

De forma predeterminada, durante la inicialización, estas dos opciones se hallan en el archivo tsconfig.json, pero están comentadas.

1. Decoradores experimentales de clase

Una función se puede utilizar como decorador de clase siempre que su tipo coincida con el de ClassDecorator. Este tipo se define en el archivo de declaración base de TypeScript (lib.d.ts):

declare type ClassDecorator = <TFunction extends Function>(  
  target: TFunction  
) => TFunction | void; 

La palabra clave type se utiliza en TypeScript para definir los alias de tipo. Se abordará en detalle más adelante en este trabajo (consulte el capítulo Sistema de tipos avanzados). La palabra clave declare se usa en archivos de definición para definir y tipar un elemento a fin de que pueda usarse en código TypeScript.

Este tipo especifica que la función decoradora acepta un parámetro cuyo tipo está restringido, mediante el uso de un genérico, para extender el de Function. El parámetro target permite recuperar el constructor de la clase.

Ejemplo:

const LogClassName: ClassDecorator = target => {  
  console.log(target.name);  
};  
  
// Log: Person  
@LogClassName 
class Person {  
  constructor(  
    public readonly firstName: string,  
    public readonly lastName: string  
  ) {}  
} 

Dado que un decorador es una función, es posible ejecutarlo...

Decoradores ECMAScript

Para utilizar decoradores ECMAScript, no es necesario habilitar las opciones de compilación en el momento de inicializar el archivo tsconfig.json. Al ser nativa, esta versión de los decoradores es compatible con TypeScript desde la versión 5.0.

Es importante tener en cuenta que esta versión de los decoradores es un primer paso en la implementación de la función y no es equivalente a los decoradores experimentales.

Entre 2016 y 2022, los diferentes miembros del grupo que trabajaba en la estandarización de decoradores en ECMAScript tuvieron muchas dificultades para llegar a un acuerdo. Esto se debió, principalmente, al hecho de que los decoradores se introdujeron en TypeScript muy temprano y luego permitieron el desarrollo de bibliotecas y frameworks utilizados con posterioridad en muchos proyectos (como Angular y NestJS, por ejemplo). Fue difícil producir un estándar en torno a los decoradores para responder correctamente al problema, redefiniendo al mismo tiempo la base impuesta por TypeScript y teniendo en cuenta las aplicaciones existentes. Se llegó a un consenso, pero cabe señalar ciertos puntos:

  • Los decoradores de parámetros no forman parte de la primera implementación de los decoradores ECMAScript. Vendrán con la versión 2.0 de la especificación.

  • Actualmente, no se ha finalizado ningún equivalente referente a metadatos en la especificación ECMAScript. Esta parte se está estudiando en otra propuesta de estandarización.

Teniendo en cuenta estos dos puntos, no es posible (en el momento en que se escribe este trabajo) aprovechar al máximo las capacidades introducidas por los decoradores experimentales a través de los decoradores nativos. Sin embargo, las capacidades de estos últimos están mejor definidas y permiten cubrir una gran parte de los escenarios en los que los decoradores son útiles.

Un decorador ECMAScript se puede definir mediante el siguiente tipo:

type Decorator<TTarget, TContext, TReturn> = (  
  target: TTarget,  
  context: TContext  
) => TReturn | void 

TTarget corresponde al tipo de miembro que se decorará. TContext contiene información relacionada con el contexto del miembro decorado. Finalmente, TReturn define el tipo de retorno esperado cuando se usa el decorador....