Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript mis-reference to _this

I tried to define some property for String.Prototype in TypeScript:

Object.defineProperty(String.prototype, 'test', {
    value: () => {
        console.log("this is a test over text " + this);
    }
})

in javaScript prototypes, this refers to the object which called the method (in this case the string value). But compiled output of the file is:

var _this = this;
Object.defineProperty(String.prototype, 'test', {
    value: function () {
        console.log("this is a test over text " + _this);
    }
});

TypeScript compiler adds variable _this and refers to it.

Is that a bug or there is a problem in my implementation?

like image 753
Morteza Tourani Avatar asked Oct 18 '15 11:10

Morteza Tourani


1 Answers

Is that a bug or there is a problem in my implementation?

No, it's how TypeScript's arrow functions work: In arrow functions, this is inherited from the context in which the function is created, rather than being set by how it's called. (Arrow functions are also in ES2015, inspired at least in part by CoffeeScript's "fat arrow" functions; I don't know the history of TypeScript and whether it was also part of the inspiration for ES2015's arrow functions or vice-versa.)

Here's a quote from the spec link above:

A function expression introduces a new dynamically bound this, whereas an arrow function expression preserves the this of its enclosing context.

Arrow function expressions are particularly useful for writing callbacks, which otherwise often have an undefined or unexpected this.

In the example

class Messenger {  
    message = "Hello World";  
    start() {  
        setTimeout(() => alert(this.message), 3000);  
    }  
};

var messenger = new Messenger();  
messenger.start();

the use of an arrow function expression causes the callback to have the same this as the surrounding 'start' method.

If you want this to depend on how the function was called, don't use an arrow function, use function:

Object.defineProperty(String.prototype, 'test', function() {
    console.log("this is a test over text " + this);
})

Also note that as nils points out the third argument to Object.defineProperty should be a property descriptor, not a function. You may have meant:

Object.defineProperty(String.prototype, 'test', {
    value: function() {
        console.log("this is a test over text " + this);
    }
});

The TypeScript transpiler doesn't change that at all; calling "testing".test() outputs "this is a test of text testing":

Object.defineProperty(String.prototype, 'test', {
    value: function() {
        snippet.log("this is a test over text " + this);
    }
});
"testing".test();
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
like image 109
T.J. Crowder Avatar answered Nov 14 '22 21:11

T.J. Crowder