Created
November 13, 2022 01:36
-
-
Save owitor/874e8a7a7e57714f2c0e161f67802fba to your computer and use it in GitHub Desktop.
Typescript decorator to execute other decorator in all methods or specific methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Handler(BreakAnalyser, { exclude: ['update'] })