34

In typescript, I can write something like this:

$('#something').fadeOut(400, (): void => {
    this.invokeAnotherMethod();
});

When compiled, TypeScript automatically makes sure this points to my class instead of the enclosed function:

var _this = this;
$('#something').fadeOut(400, function() {
    _this.invokeAnotherMethod();
});

However, what about when I need to access the real this instead of the outer _this? Is there syntax to reference it? For example, how could I write code that would compile to the following:

var _this = this;
$('#something').fadeOut(400, function() {
    $(this).data('specialhide', true);
    _this.invokeAnotherMethod();
});

Is it possible?

danludwig
  • 46,965
  • 25
  • 159
  • 237

3 Answers3

28

You would need to avoid the fat-arrow syntax to do this as you don't want to preserve the lexical scope of this.

var _me = this;
$('#something').fadeOut(400, function () {
    _me.invokeAnotherMethod();
    $(this).data('specialhide', true);
});

In this example I have used _me rather than _this to avoid any collisions with TypeScript generated variables. I have also avoided self, to avoid confusion with window.self (thanks RockResolve).

The Why!

The ECMAScript 6 specification features Arrow Function Definitions - it is where the TypeScript language has taken this feature from. When TypeScript targets ECMAScript 6 in the future, it will leave in the () => syntax - so they can't make it work with both contexts of this without breaking future compatibility.

Even though you could imagine how they could change the TypeScript compiler to make both _this and this available in ECMAScript 3 or 5, it would actually become a problem in version 6.

Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Thanks, that is exactly what I am currently doing, stashing `self` and using it without an arrow function. Just wondered if there was another way. – danludwig Dec 18 '12 at 14:02
  • 4
    Beware of using the word "self". The built in window variable "self" can cause confusion (it got me while debugging), better to choose another word. – RockResolve Apr 30 '15 at 04:20
2

I figured a way out, as described here in my answer at: How can I preserve lexical scope in TypeScript with a callback function

This is a nicer way of achieving the closure that Steve Fenton did in his answer. I prefer it because the method signature documents usage.

Basically, use a method like so:

fadeOutLambda(outerThis: YourClass): {(d: number, i: number): number}{
    return function(d: number, i: number){
        // Put your "fadeOut" logic here
        // refer to "this" to mean the dynamically scoped "this"
        // refer to "outerThis" to refer to the class instance
        alert(outerThis); // lexically scoped class instance
        alert(this); // dynamically scoped context caller
        return 999;
    }
}
Community
  • 1
  • 1
Eric
  • 663
  • 7
  • 18
  • You can also avoid having to pass in the 'outerThis' by simply taking a local variable of this, thereby simplifying the call to fadeOutLambda. e.g. fadeOutLambda() { var that = this; return function() { ... }; } – Tyson Aug 04 '15 at 23:49
  • 1
    I made a "do as I say, not as I do", because the way Tyson offers is the way I do it for most of my lambda functions. The use of outerThis as an argument is to make an explicit reminder to the caller that the method cannot be called as is to perform the computation, and must have its resulting function called. That is, we call it as this.fadeOutLambda(this)(). The IDE reminds the programmer of this, whereas if we use "var that = this" for a closure, there is no reminder. Defensive programming against using fadeOutLambda() instead of this.fadeOutLambda()(), should they want to call it directly. – Eric Aug 10 '15 at 22:52
0

Let me provide another solution not using lambda. You can attach the main this as a property (called me in this example).

class MyClass
{
    constructor()
    {
        var button: HTMLElement = document.getElementById("buttonID");
        (button as any).me = this;
        button.onclick = this.buttonClick;
    }

    private buttonClick(e): boolean
    {
        var me: MyClass = (this as any).me;

        // Implementation using this (the button) and me (the class)

        return false;
    }
}
Riera
  • 369
  • 1
  • 2
  • 12