Javascript Decorators
A decorator starts with an @
symbol and can be put in front of classes and class member variable and methods.
It doesn't seems to be able to used on free functions yet. There seems to be some controversies
Under the hood, a decorator is a function takes a decorated value and a context. A decorator can do four things
- Change the decorated entity by mutating
value
- Replace the decorated entity by returning a compatible value
- Exposing access to the decorated entity to others (via
context.access
) - Processing the decorated entity and its container (if it has one), after both exist: That functionality is provided by
context.addInitializer
.
loggedMethod
decorator exampleFor example, the following example add log to entry and exit of the decorated method
function loggedMethod(originalMethod, context) { const methodName = String(context.name); function replacementMethod(this, ...args) { console.log(`LOG: Entering method '${methodName}'.`) const result = originalMethod.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`) return result; } return replacementMethod; }
it can be used as following
class Person { ... @loggedMethod greet() { ... } }
The context
parameter contains some additional information. We can see it in Decorator
’s TypeScript type.
Decorator's Typescript Type Signature
type Decorator = ( value: DecoratedValue, // only fields differ context: { kind: string; // which kind of JS construct name: string | symbol; addInitializer(initializer: () => void): void; // Don’t always exist: static: boolean; private: boolean; access: {get: () => unknown, set: (value: unknown) => void}; } ) => void | ReplacementValue; // only fields differ
Types In TypeScript
Typescript provides types such as ClassMethodDecoratorContext
which we can help to write decorators.
well typed
loggedMethod
decoratorfunction loggedMethod<This, Args extends any[], Return>( target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return> ) { const methodName = String(context.name); function replacementMethod(this: This, ...args: Args): Return { console.log(`LOG: Entering method '${methodName}'.`) const result = target.call(this, ...args); console.log(`LOG: Exiting method '${methodName}'.`) return result; } return replacementMethod; }
Class Method Decorators
Example: mimic bound method in JavaScript
See also
References
parent: JavaScript, TypeScript tags:typescriptjavascript