I discovered a clever technique using ReflectMetadata:
A special decorator
export const A =
() =>
(consumer: any, methodName: string, descriptor: PropertyDescriptor) => {
const existingAMethods =
Reflect.getOwnMetadata("A_METHODS", consumer.constructor) || []
const options: {
consumer,
methodName,
descriptor
}
existingAMethods.push(options)
Reflect.defineMetadata(
"A_METHODS",
existingAMethods,
consumer.constructor
)
}
Another unique decorator
export const B =
() =>
(consumer: any, methodName: string, descriptor: PropertyDescriptor) => {
const existingBMethods =
Reflect.getOwnMetadata("B_METHODS", consumer.constructor) || []
const options: {
consumer,
methodName,
descriptor
}
existingBMethods.push(options)
Reflect.defineMetadata(
"B_METHODS",
existingBMethods,
consumer.constructor
)
}
Introducing the final one called "C"
export const C = () =>
(constructor: Function): void => {
const options =
Reflect.getOwnMetadata("A_METHODS", constructor) || []
// Perform actions related to A
const options =
Reflect.getOwnMetadata("B_METHODS", constructor) || []
// Perform actions related to B
}
Application example
@C()
class Example {
@A()
public method1(): void { ... }
@B()
public method2(): void { ... }
@A()
public method3(): void { ... }
}