Skip to content

Instantly share code, notes, and snippets.

@owitor
Created November 13, 2022 01:36
Show Gist options
  • Save owitor/874e8a7a7e57714f2c0e161f67802fba to your computer and use it in GitHub Desktop.
Save owitor/874e8a7a7e57714f2c0e161f67802fba to your computer and use it in GitHub Desktop.
Typescript decorator to execute other decorator in all methods or specific methods
import 'reflect-metadata';
export interface DecorateAllOptions {
deep?: boolean;
exclude?: string[];
excludePrefix?: string;
}
/**
* Apply the given decorator to all class methods
*
* @param decorator Method decorator to apply to all methods of a class
* @param options
* @param {string[]} options.exclude array of method names that won't be decorated
* @param {boolean} options.deep if true, also decorates methods of the extended classes (recursively)
*/
export const Handler = (
decorator: MethodDecorator,
options: DecorateAllOptions = {}
) => {
return (target: any) => {
let descriptors = Object.getOwnPropertyDescriptors(target.prototype);
if (options.deep) {
let base = Object.getPrototypeOf(target);
while (base.prototype) {
const baseDescriptors = Object.getOwnPropertyDescriptors(
base.prototype
);
descriptors = { ...baseDescriptors, ...descriptors };
base = Object.getPrototypeOf(base);
}
}
for (const [propName, descriptor] of Object.entries(descriptors)) {
const isMethod =
typeof descriptor.value == 'function' && propName != 'constructor';
if (options.exclude?.includes(propName)) {
continue;
}
if (options.excludePrefix && propName.startsWith(options.excludePrefix)) {
continue;
}
if (!isMethod) {
continue;
}
const originalMethod = descriptor.value;
decorator(target, propName, descriptor);
if (originalMethod != descriptor.value) {
copyMetadata(originalMethod, descriptor.value);
}
Object.defineProperty(target.prototype, propName, descriptor);
}
};
};
/**
* Copies all metadata from one object to another.
* Useful for overwriting function definition in
* decorators while keeping all previously
* attached metadata
*
* @param from object to copy metadata from
* @param to object to copy metadata to
*/
export function copyMetadata(from: any, to: any) {
const metadataKeys = Reflect.getMetadataKeys(from);
metadataKeys.map((key) => {
const value = Reflect.getMetadata(key, from);
Reflect.defineMetadata(key, value, to);
});
}
@owitor
Copy link
Author

owitor commented Nov 13, 2022

@Handler(BreakAnalyser, { exclude: ['update'] })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment