Even though Decorators are still experimental (currently stage-2), let's figure out what they are, how we'd use them and whether it changes the way we think.
π§ To enable experimental support for decorators, you must enable the
experimentalDecorators
compiler option intsconfig.json
or in the command line usingtsc --experimentalDecorators
.
So what is a Decorator? A function that can be applied to a class, method, accessor, property or parameter.
π Before we continue, know that there are two variations: Decorators and Decorator Factories
// Oh look, a function, nothing special here as it just receives a `target` parameter
function basicDecorator(target) {
// Decorator magic happens here
}
// A function that returns a function, still nothing special here!
function decoratorFactory(someValue: string) {
return function(target) {
// Decorator magic happens here
}
}
// Usage
@basicDecorator
@decoratorFactory("some value")
class MyClass {
//The expressions for each decorator are evaluated top-to-bottom.
//The results are then called as functions from bottom-to-top.
}
Topics
π Class Decorators
π Method Decorators
π Accessor Decorators
π Property Decorators
π Parameter Decorators
π Class Decorators
Class decorators allow you to hook into the constructor of a class and allowing us to perform arbitrary code at runtime (before the class is executed/instantiated).
π You can change the prototype of the class (which is highly discouraged) so tread lightly!
function sealed<T>(target: T) {
Object.seal(target);
Object.seal(target.prototype);
}
@sealed
class MyClass {
// class code goes here
}
Would you rather see it all in action? Use the Class Decorator Playground.
π Method Decorators
Method decorators allow us to completely override an existing method or better yet add some logic before it's executed (like deboucing, logging, etc.)
// Method decorator
function myMethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// Update method descriptor, replace the method, etc.
}
class MyClass {
@myMethodDecorator
doAction() {
// Do your thing here
console.log("doAction has been called");
}
}
Would you rather see it all in action? Use the Method Decorator Playground.
π Accessor Decorators
Unfortunately not many developers use Accessors enough (hopefully this entices you to get more familiar with them). As with Method decorators, it allows us to modify the descriptor (which includes the getters and setters).
function myAccessorDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// Update method descriptor, replace the getter and/or setter, etc.
}
class MyClass {
@myAccessorDecorator
get myValue() {
// internal set logic here
}
}
Would you rather see it all in action? Use the Accessor Decorator Playground.
π Hold up! The next two decorators are only really 'useful' when using external libraries like reflect-metadata (which adds polyfills for the experimental metadata API), as they don't receive a property descriptor parameter. Once decorators are fully supported, will the
reflect-metadata
library proposed for adoption.
π Property Decorators
I'm sure you've picked up on the pattern already, simply add the decorator before the property. If you're not going to use an external library, then I'd pass on using a property decorator and instead use an accessor or method decorator.
function myPropertyDecorator(target: any, propertyKey: string) {
// Use an external library like `reflect-metadata`
}
class MyClass {
@myPropertyDecorator
myValue: string;
constructor(value: string) {
this.myValue = value;
}
}
Would you rather see it all in action? Use the Property Decorator Playground.
π Parameter Decorators
As before simply add the decorator before your parameter of choice. You will notice that we have a new parameter called parameterIndex
, which is pretty self explanatory.
function myParameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
// Use an external library like `reflect-metadata`
console.log(...arguments);
}
class MyClass {
doAction(value: string, @myParameterDecorator anotherValue: number) {
console.log(`doAction called with: ${value} & ${anotherValue}`);
}
}
Would you rather see it all in action? Use the Parameter Decorator Playground.
Proof read & edited by my beautiful wife β€!
Remember to #KeepHavingFun
Top comments (0)