son que los decorators decoradores decorador crear componentes componente como clases typescript decorator

typescript - decorators - que son los componentes de angular



¿Cómo implementar un decorador mecanografiado? (3)

TypeScript 1.5 ahora tiene decorators .

¿Podría alguien proporcionar un ejemplo simple que demuestre la forma correcta de implementar un decorador y describir lo que significan los argumentos en las posibles firmas de decorador válidas?

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void; declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void; declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

Además, ¿hay algunas consideraciones de mejores prácticas que se deben tener en cuenta al implementar un decorador?


Terminé jugando con decoradores y decidí documentar lo que descubrí para cualquiera que quiera aprovechar esto antes de que salga la documentación. Siéntase libre de editar esto si ve algún error.

Puntos generales

  • Se llama a los decoradores cuando se declara la clase, no cuando se instancia un objeto.
  • Se pueden definir múltiples decoradores en la misma Clase / Propiedad / Método / Parámetro.
  • No se permiten decoradores en los constructores.

Un decorador válido debe ser:

  1. Asignable a uno de los tipos de decorador ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator ).
  2. Devuelve un valor (en el caso de decoradores de clase y decorador de métodos) que se puede asignar al valor decorado.

Reference

Método / Decorador de accesorios formal

Parámetros de implementación:

  • target : El prototipo de la clase ( Object ).
  • propertyKey : el nombre del método ( string | symbol ).
  • descriptor : Un TypedPropertyDescriptor : si no está familiarizado con las claves de un descriptor, recomendaría leerlo en esta documentación en Object.defineProperty (es el tercer parámetro).

Ejemplo: sin argumentos

Utilizar:

class MyClass { @log myMethod(arg: string) { return "Message -- " + arg; } }

Implementación:

function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) { const originalMethod = descriptor.value; // save a reference to the original method // NOTE: Do not use arrow syntax here. Use a function expression in // order to use the correct value of `this` in this method (see notes below) descriptor.value = function(...args: any[]) { // pre console.log("The method args are: " + JSON.stringify(args)); // run and store result const result = originalMethod.apply(this, args); // post console.log("The return value is: " + result); // return the result of the original method (or modify it before returning) return result; }; return descriptor; }

Entrada:

new MyClass().myMethod("testing");

Salida:

Los argumentos del método son: ["prueba"]

El valor de retorno es: Mensaje: prueba

Notas:

  • No utilice la sintaxis de flecha al configurar el valor del descriptor. El contexto de this no será la instancia si lo hace.
  • Es mejor modificar el descriptor original que sobrescribir el actual devolviendo un nuevo descriptor. Esto le permite usar múltiples decoradores que editan el descriptor sin sobrescribir lo que hizo otro decorador. Hacer esto le permite usar algo como @enumerable(false) y @log al mismo tiempo (Ejemplo: Bad vs Good )
  • Útil : El argumento de tipo de TypedPropertyDescriptor se puede usar para restringir qué firmas de método ( Ejemplo de método ) o firmas de acceso ( Ejemplo de acceso) se puede poner el decorador.

Ejemplo: con argumentos (Fábrica de decoradores)

Al usar argumentos, debe declarar una función con los parámetros del decorador y luego devolver una función con la firma del ejemplo sin argumentos.

class MyClass { @enumerable(false) get prop() { return true; } } function enumerable(isEnumerable: boolean) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => { descriptor.enumerable = isEnumerable; return descriptor; }; }

Decorador de métodos estáticos

Similar a un decorador de métodos con algunas diferencias:

  • Su parámetro target es la función del constructor en sí y no el prototipo.
  • El descriptor se define en la función constructora y no en el prototipo.

Decorador de clase

@isTestable class MyClass {}

Parámetro de implementación:

  • target : la clase en la que se declara el decorador ( TFunction extends Function ).

Ejemplo de uso : uso de la API de metadatos para almacenar información en una clase.

Decorador de propiedades

class MyClass { @serialize name: string; }

Parámetros de implementación:

  • target : El prototipo de la clase ( Object ).
  • propertyKey : el nombre de la propiedad ( string | symbol ).

Ejemplo de uso : crear un @serialize("serializedName") y agregar el nombre de la propiedad a una lista de propiedades para serializar.

Decorador de parámetros

class MyClass { myMethod(@myDecorator myParameter: string) {} }

Parámetros de implementación:

  • target : El prototipo de la clase ( Function parece que Function ya no funciona. Debería usar any u Object aquí ahora para usar el decorador dentro de cualquier clase. O especificar el tipo de clase (s) que desea restringirlo a)
  • propertyKey : el nombre del método ( string | symbol ).
  • parameterIndex : el índice del parámetro en la lista de parámetros de la función ( number ).

Ejemplo simple

Ejemplo (s) detallado (s)


Una cosa importante que no veo en las otras respuestas:

Fábrica de decoradores

Si queremos personalizar cómo se aplica un decorador a una declaración, podemos escribir una fábrica de decoradores. Una fábrica de decoradores es simplemente una función que devuelve la expresión que el decorador invocará en tiempo de ejecución.

// This is a factory, returns one of ClassDecorator, // PropertyDecorator, MethodDecorator, ParameterDecorator function Entity(discriminator: string): { return function(target) { // this is the decorator, in this case ClassDecorator. } } @Entity("cust") export class MyCustomer { ... }

Consulte el capítulo Decoradores del manual de TypeScript.


class Foo { @consoleLogger Boo(name:string) { return "Hello, " + name } }

  • target: prototipo de la clase en el caso anterior es "Foo"
  • propertyKey: nombre del método llamado, en el caso anterior "Boo"
  • descriptor: la descripción del objeto => contiene propiedad de valor, que a su vez es la función en sí misma: function (name) {return ''Hello'' + name; }

Podría implementar algo que registre cada llamada a la consola:

function consoleLogger(target: Function, key:string, value:any) { return value: (...args: any[]) => { var a = args.map(a => JSON.stringify(a)).join(); var result = value.value.apply(this, args); var r = JSON.stringify(result); console.log(''called method'' + key + '' with args '' + a + '' returned result '' + r); return result; } }