TypeScript "this" scoping issue when called in jquery callback

Viewed 60265

I'm not sure of the best approach for handling scoping of "this" in TypeScript.

Here's an example of a common pattern in the code I am converting over to TypeScript:

class DemonstrateScopingProblems {
    private status = "blah";
    public run() {
        alert(this.status);
    }
}

var thisTest = new DemonstrateScopingProblems();
// works as expected, displays "blah":
thisTest.run(); 
// doesn't work; this is scoped to be the document so this.status is undefined:
$(document).ready(thisTest.run); 

Now, I could change the call to...

$(document).ready(thisTest.run.bind(thisTest));

...which does work. But it's kinda horrible. It means that code can all compile and work fine in some circumstances, but if we forget to bind the scope it will break.

I would like a way to do it within the class, so that when using the class we don't need to worry about what "this" is scoped to.

Any suggestions?

Update

Another approach that works is using the fat arrow:

class DemonstrateScopingProblems {
    private status = "blah";

    public run = () => {
        alert(this.status);
    }
}

Is that a valid approach?

4 Answers

Another solution that requires some initial setup but pays off with its invincibly light, literally one-word syntax is using Method Decorators to JIT-bind methods through getters.

I've created a repo on GitHub to showcase an implementation of this idea (it's a bit lengthy to fit into an answer with its 40 lines of code, including comments), that you would use as simply as:

class DemonstrateScopingProblems {
    private status = "blah";

    @bound public run() {
        alert(this.status);
    }
}

I haven't seen this mentioned anywhere yet, but it works flawlessly. Also, there is no notable downside to this approach: the implementation of this decorator -- including some type-checking for runtime type-safety -- is trivial and straightforward, and comes with essentially zero overhead after the initial method call.

The essential part is defining the following getter on the class prototype, which is executed immediately before the first call:

get: function () {
    // Create bound override on object instance. This will hide the original method on the prototype, and instead yield a bound version from the
    // instance itself. The original method will no longer be accessible. Inside a getter, 'this' will refer to the instance.
    var instance = this;

    Object.defineProperty(instance, propKey.toString(), {
        value: function () {
            // This is effectively a lightweight bind() that skips many (here unnecessary) checks found in native implementations.
            return originalMethod.apply(instance, arguments);
        }
    });

    // The first invocation (per instance) will return the bound method from here. Subsequent calls will never reach this point, due to the way
    // JavaScript runtimes look up properties on objects; the bound method, defined on the instance, will effectively hide it.
    return instance[propKey];
}

Full source


The idea can be also taken one step further, by doing this in a class decorator instead, iterating over methods and defining the above property descriptor for each of them in one pass.

Related