Reputation: 33
I'm working through an example of a method decorator in TypeScript, trying to understand how they function. The example can be found here: https://fireship.io/lessons/ts-decorators-by-example/#method-decorator
Essentially there's a Confirmation decorator that the option chosen should be added to an array. This decorator is called placed twice before the method it decorates:
const confirmStatement1 = `Are you sure?`;
const confirmStatement2 = `Are you super sure?`;
class IceCreamComponent {
toppings = [];
@Confirmable(confirmStatement1)
@Confirmable(confirmStatement2)
addTopping(topping) {
this.toppings.push(topping);
}
}
The Decorator code is:
// Method Decorator
function Confirmable(message: string) {
return function (target: Object, key: string | symbol, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
const allow = Math.random() > 0.5 ? true : false;
console.log(`${message} - allowed value is ${allow}`);
if (allow) {
const result = original.apply(this, args);
return result;
} else {
console.log(`Stopping others running...`);
return null;
}
};
return descriptor;
};
}
When I do some calling of the code like this:
const myIceCream = new IceCreamComponent();
const possibleToppings = ["Flake", "Salted Caramel Syrup", "Raspberry Syrup", "Chocolate Freeze", "Sprinkles"];
for (let i = 0; i < 5; i++) {
myIceCream.addTopping(possibleToppings[i]);
}
console.log(myIceCream.toppings);
I get these results:
Are you sure? - allowed value is true
Are you super sure? - allowed value is true
Are you sure? - allowed value is true
Are you super sure? - allowed value is false
Stopping others running...
Are you sure? - allowed value is true
Are you super sure? - allowed value is true
Are you sure? - allowed value is false
Stopping others running...
Are you sure? - allowed value is true
Are you super sure? - allowed value is true
[ 'Flake', 'Raspberry Syrup', 'Sprinkles' ]
Obviously, I have worked out that if allow
is false then the anonymous function returns null - and that seems to stop the other decorator from being evaluated.
My question is why? I can't seem to find an answer anywhere...what am I missing?
Upvotes: 1
Views: 838
Reputation: 11283
The returned value from descriptor.value = function (...args) {
doesn't pass to the next decorator. It is the original.apply(this, args);
invoke actually calls another decorator.
I added more verbose logging as it's not intuitive at first.
console.log('pre original.apply(this, args);')
const result = original.apply(this, args);
console.log('after original.apply(this, args);')
return result;
Transpiled code in snippet
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
const confirmStatement1 = `Are you sure?`;
const confirmStatement2 = `Are you super sure?`;
function Confirmable(message) {
return function (target, key, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
const allow = Math.random() > 0.5 ? true : false;
console.log(`${message} - allowed value is ${allow}`);
if (allow) {
console.log('pre original.apply(this, args);');
const result = original.apply(this, args);
console.log('after original.apply(this, args);');
return result;
}
else {
console.log(`Stopping others running...`);
return null;
}
};
return descriptor;
};
}
class IceCreamComponent {
constructor() {
this.toppings = [];
}
addTopping(topping) {
this.toppings.push(topping);
}
}
__decorate([
Confirmable(confirmStatement1),
Confirmable(confirmStatement2)
], IceCreamComponent.prototype, "addTopping", null);
const myIceCream = new IceCreamComponent();
const possibleToppings = ["Flake", "Salted Caramel Syrup", "Raspberry Syrup", "Chocolate Freeze", "Sprinkles"];
for (let i = 0; i < 5; i++) {
myIceCream.addTopping(possibleToppings[i]);
}
console.log(myIceCream.toppings);
Upvotes: 1