Casares
Casares

Reputation: 51

TypeScript this scope don't working within a closure

I'm a beginner development in TypeScript. I thought typescript could solve all my problems with this scope in JavaScript, but it doesn't seem so. See the example code below:

class Car{
    private name : string;
    private velocity: number;
    constructor ( name:string){
        this.name = name;
        this.velocity = 0;
    }

    private speedUp (incrementer: number){
        this.velocity += incrementer;
    }

    public pressAccelerator (power: number){
        console.log(this); //here this is a Car object       
        setTimeout(
            function(){
                // here is a Timeout object - WTF!!! 
                // was't this typeScript?
                // what's up whit my "this" object scope?
                console.log(this);
                this.speedUp(power*1.34); //TypeError: this.speedUp is not a function              
            },    
            1000
        );
    }
    public getVelocity():number{
        return this.velocity;
    }
}

let myCar = new Car("Mercedes");
myCar.pressAccelerator(2);
console.log(myCar.getVelocity());

Why does this in the this.speedUp method, doesn't point to the car object?

Which is a practical way to solve?

Upvotes: 1

Views: 1175

Answers (3)

Jeroen van Langen
Jeroen van Langen

Reputation: 22083

You suggest to get use to the arrow notation:

class Car {
    private name : string;
    private velocity: number;

    constructor (name: string) {
        this.name = name;
        this.velocity = 0;
    }

    private speedUp = (incrementer: number): void => {
        console.log(this);
        this.velocity += incrementer;
    } 

    public pressAccelerator = (power: number): void => {

        console.log(this); //here this is a Car object       
        setTimeout(() => this.speedUp(power*1.34), 1000);
    }
    public getVelocity = (): number => {
        return this.velocity;
    }
}

let myCar = new Car("Mercedes");
myCar.pressAccelerator(2);
console.log(myCar.getVelocity());

Which will be compiled as:

var Car = /** @class */ (function () {
    function Car(name) {
        var _this = this;
        this.speedUp = function (incrementer) {
            console.log(_this);
            _this.velocity += incrementer;
        };
        this.pressAccelerator = function (power) {
            console.log(_this); //here this is a Car object       
            setTimeout(function () { return _this.speedUp(power * 1.34); }, 1000);
        };
        this.getVelocity = function () {
            return _this.velocity;
        };
        this.name = name;
        this.velocity = 0;
    }
    return Car;
}());
var myCar = new Car("Mercedes");
myCar.pressAccelerator(2);
console.log(myCar.getVelocity());

As you can see, the typescript will fix that closure. You can read more here


A problem it won't fix is if you define a function with a scope within the timer callback: (non arrow function)

setTimeout(
    function(){
        // here is a Timeout object - WTF!!! 
        // was't this typeScript?
        // what's up whit my "this" object scope?
        console.log(this);
        this.speedUp(power*1.34); //TypeError: this.speedUp is not a function              
    },    
    1000
);

Upvotes: 1

Heretic Monkey
Heretic Monkey

Reputation: 12114

Use an arrow function, which retains the this:

public pressAccelerator (power: number){
    console.log(this); //here this is a Car object       
    setTimeout(
        () => {
            console.log(this);
            this.speedUp(power*1.34);
        },    
        1000
    );
}

Upvotes: 2

StephaneM
StephaneM

Reputation: 4899

I don't know if it's elegant but this works:

public pressAccelerator (power: number){
    console.log(this); //here this is a Car object   
    let self = this;
    setTimeout(
        function(){
            console.log(self);
            self.speedUp(power*1.34);
        },    
        1000
    );
}

Upvotes: 0

Related Questions