I have read "How to implement a typescript decorator?" and multiple sources but there is something that i have nor been able to do with decorators.
class FooBar {
public foo(arg): void {
console.log(this);
this.bar(arg);
}
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
If we invoke the function foo
:
var foobar = new FooBar();
foobar.foo("test");
The object FooBar
is logged in the console by console.log(this);
in foo
The string "FooBar {foo: function, bar: function} bar test"
is logged in the console by console.log(this, "bar", arg);
in bar
.
Now let's use a decorator:
function log(target: Function, key: string, value: any) {
return {
value: (...args: any[]) => {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args); // How to avoid hard coded this?
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
We use the same function but decorated:
class FooBar {
@log
public foo(arg): void {
console.log(this);
this.bar(arg);
}
@log
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
And we invoke foo
as we did before:
var foobarFoo = new FooBar();
foobarFooBar.foo("test");
The objectWindow
is logged in the console by console.log(this);
in foo
And bar
is never invoked by foo
because this.bar(arg);
causes Uncaught TypeError: this.bar is not a function
.
The problem is the hardcoded this
inside the log
decorator:
value.value.apply(this, args);
How can I conserve the original this
value?
A decorator factory can be written in the following manner: function color(value: string) { // this is the decorator factory. return function (target) { // this is the decorator.
A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.
To define a general purpose decorator that can be applied to any function we use args and **kwargs . args and **kwargs collect all positional and keyword arguments and stores them in the args and kwargs variables. args and kwargs allow us to pass as many arguments as we would like during function calls.
Decorators can be chained A Decorator function is used only to format the output of another function dec keyword is used for decorating a function Decorators always return None” Code Answer.
Don't use an arrow function. Use a function expression:
function log(target: Object, key: string, value: any) {
return {
value: function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
That way it will use the function's this
context instead of the value of this
when log is called.
By the way, I would recommend editing the descriptor/value parameter and return that instead of overwriting it by returning a new descriptor. That way you keep the properties currently in the descriptor and won't overwrite what another decorator might have done to the descriptor:
function log(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
var originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = originalMethod.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
};
return descriptor;
}
More details in this answer - See the "Bad vs Good" example under "Example - Without Arguments > Notes"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With