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:
- Asignable a uno de los tipos de Decorador (
ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
).
- Devuelve un valor (en el caso de decoradores de clase y decorador de métodos) que se puede asignar al valor decorado.
Referencia
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
: A TypedPropertyDescriptor
- Si no está familiarizado con las claves de un descriptor, recomendaría leerlo en esta documentación sobre 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: Malo vs Bueno )
- Útil : El argumento tipo de
TypedPropertyDescriptor
se puede usar para restringir las firmas de métodos ( Ejemplo de método ) o las firmas de acceso ( Ejemplo de acceso ) en que 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
target
parámetro es la función constructora 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")
decorador 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
o Object
aquí ahora para usar el decorador dentro de cualquier clase. O especificar el tipo de clase a la que desea restringirlo)
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)
@Injectable
en un decorador, consulte