Saturday, July 09, 2016

TypeScript: Avoid Using Lambda in CallBack, "this" is not Captured

Symptom:

You have created a TypeScript class, and assigned a callback to an object implemented in Lambda expression (or have been suggested by Reshaper or IntelliJ to convert it to a lambda expression).

See the example below and what the problem is about to become much more clear.

The Issue

The issue is that the Lambda captures "this" in a different way than the anonymous function (). This is actually clearly documented under the Arrow Functions section in the Mozilla MDN page.

But if you are like me it is much easier to understand by actually coding the situation.

The Explanation of The Example:

I have created a simple Main class in which a callback "whatIsThis()" can be externally installed after the class has been instantiated. Then by calling whatReallyIsThis(), it will in turn calls the installed whatIsThis(). This is a fairly typical scenario, I would say.

If you are not aware of all of the implications of this, you would (and I have) thought that the class member property "s" should be accessible from within the callback in all of the scenarios.

The ones that works are;

  • If the callback function is is a function() and not an arrow function like =>(). This will not work.
  • Also, you can try this yourself, any attempt to pass "this" as main like Apply, Call or Bind to the lambda function will have no effect. 
Lessons Learned

You should treat lambda as a whole new beast of function. It appears that they tried to fix the variable notion of "this" in lambda and it only captures "this" in the line of code where it was created. What throws you off is that you've learned about "this" with anonymous functions, and assumed it works the same. Be aware of this major difference!


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Main {
    constructor(public s: string) {}
    whatIsThis:  () => void;
    whatReallyIsThis() {
        this.whatIsThis();
    }
}

let main = new Main("works!");

console.log("Anonymouns func - this points to Main instance!");

main.whatIsThis = function() { 
    console.log("This " + this.s);
}

main.whatReallyIsThis(); // This works.

console.log("Using the Labmda - this does not work!");

main.whatIsThis = () => {
    console.log("This s is " + this.s);
}

main.whatReallyIsThis(); // This s is undefined

console.log("Finally, what is this is all about");
let that = main;
exports.s = "in Global scope"; // Yup, it's in the Global!
main.whatIsThis = =>()  {
    console.log"Thi(s s is " + this.s); 
    console.log("That.s " + that.s); 
}
main.whatReallyIsThis(); // This s is in Global scope, That.s works.


Post a Comment