Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid hard coded this? in Decorators

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?

like image 587
Remo H. Jansen Avatar asked May 19 '15 15:05

Remo H. Jansen


People also ask

What is the code for creating a decorator?

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.

What are decorators how are they useful?

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.

How do you declare a decorator in Python?

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.

Which of the following is true about decorators in Python?

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.


1 Answers

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"

like image 181
David Sherret Avatar answered Nov 07 '22 06:11

David Sherret