Study Note with Decorator In TypeScript (2) - Method Decorator
June 03, 2019
Inroduction
Last week I have introduced simple property decorator. This week, I would like to talk about method decorators and provide a simple example to help understanding.
Definition of Method Decorator
According to the definition from TypeScript documentation
A Method Decorator is declared just before a method declaration. The decorator is applied to the Property Descriptor for the method, and can be used to observe, modify, or replace a method definition. A method decorator cannot be used in a declaration file, on an overload, or in any other ambient context (such as in a declare class).
In my understanding, a method decorator is just a function which takes the decorated function, its name and properties as parameters and then process the target by adding your own logics.
Simple Example
Step 1: Know what parameters are needed for a method decorator
Let’s see a simple example of method decorator
class MyClass {
public message: String = "I am called";
@MyMethodDecorator
public myMethod() {
console.log(this.message);
}
}
function MyMethodDecorator(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log("target: ", target);
console.log("propertyKey: ", propertyKey);
console.log("descriptor: ", descriptor);
}
const myClass = new MyClass();
myClass.myMethod();
The output is printed as shown below:
target: MyClass { myMethod: [Function] }
propertyKey: myMethod
descriptor: { value: [Function],
writable: true,
enumerable: true,
configurable: true }
I am called
As shown, the 3 parameters required for method decorator are target
: the class itself , propertyKey
: the name of the method and descriptor
: the meta-data to describe this method, e.g. writable, enumerable or configurable. Interestingly, if you are aware of Object.defineProperty(obj, prop, descriptor)
function, you would notice that the 3rd parameter of this function also accepts a data strcture called descriptor
.
Step 2: Let’s implement something in the method decorator
Take writable
as an example. This property defines whether the target is allowed to be written. If it’s set to false, you can never change the value of the target anymore.
For example, let’s change the MyMethodDecorator()
as shown below
function MyMethodDecorator(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.writable = false;
}
At the end of the program, change the last 2 lines of codes to the codes below.
const myClass = new MyClass();
myClass.myMethod();
myClass.myMethod = function() {
console.log("I am changed");
};
myClass.myMethod();
After we call myMethod()
, which will print “I am called”, we then assign the myMethod
to point to another function, which will print "I am changed"
. Then we call the method again. You may probably think, at the second time, it will print "I am changed"
.
However, the actual output of the program is
I am called
I am called
It is because we have set the descriptor.writable
to false
. From this example, we can know how modifying the descriptor in a decorator would affect the method.
Step 3: Remove the decorator and see how it goes
Now let’s remove the @MyMethodDecorator
from the myMethod
method and run the program again.
class MyClass {
public message: String = "I am called";
//@MyMethodDecorator
public myMethod() {
console.log(this.message);
}
}
Now the program should print 2 different lines of words as below
I am called
I am changed
Conclusion
This article briefly introduces how it actually works with a method decorator. I try to use the simplest example to explain a basic usage of it. In real life, we can actually utilise this feature to do many creative works. If you are interested, you may read through the source code of MobX because it frequently uses decorators. Once of a commonly used method decorator in MobX is computed
, you may read the MobX source code here to see how a method decorator creates magic!