Cannot call private function from constructor in TypeScript

Viewed 1106

I have a simple TypeScript class with a private function that should get called, when the user clicks a button. The click event is bound via jQuery click() event in the constructor

HTML

<div id="foobar">
  <h2>Foo</h2>
  <button type="button">Bar</button>
</div>

TS

$(() => {
    var foo = new Bar($("#foobar"));
})

class Bar {
    private view: JQuery;
    private button: JQuery;

    constructor(view: JQuery) {
            // Fields
        this.view = view;
        this.button = view.find("button");

        // Events
        this.button.click(() => { this.buttonClick() });
    }

    private buttonClick() {
        this.view.find("h2").css("background-color", "red");
    }
}

https://jsfiddle.net/z4vo5u5d/18781/

But somehow, when executing the script, the console complains that buttonClick is not a function. What am I missing here?

I suppose it's a problem with "this" in TypeScript. But I can't figure out why.

Edited: as @Amadan mentioned:

this.button.click(() => { this.buttonClick() });

is translated incorrectly by jsfiddle into

this.button.click(function () { this.buttonClick(); });

Meanwhile, the compiler at typescriptlang.org/play translates it correctly as:

var _this = this;
...
this.button.click(function () { _this.buttonClick(); });
1 Answers

I think what's going wrong here is what I like to call a scoping issue.

In old versions of TypeScript, like the one JSFiddle uses, the scope of this is not the same everywhere you use it. It's a dynamic variable that changes depending on where it's called. The this.buttonClick() in the button's event function evaluates to the buttonClick() function of the button itself, which it doesn't have, since it belongs to Bar.

Try assigning a const self = this; value just below your constructor(view: JQuery) { and replace all occurrences of this with self in your constructor function.

This makes sure that the object that self evaluates to is always the object itself, not the current context, which might not be what you're aiming at.

class Bar {
    private view: JQuery;
    private button: JQuery;

    constructor(view: JQuery) {
        const self = this;

        // Fields
        self.view = view;
        self.button = view.find("button");

        // Events
        self.button.click(() => { self.buttonClick() });
    }

    private buttonClick() {
        this.view.find("h2").css("background-color", "red");
    }
}

That should work perfectly. I've ran into this problem many times before. I've gotten used to declaring const self = this; in every function I write.

Edited: as @Amadan mentioned:

this.button.click(() => { this.buttonClick() });

is translated incorrectly by jsfiddle into

this.button.click(function () { this.buttonClick(); });

Meanwhile, the compiler at typescriptlang.org/play translates it correctly as:

var _this = this;
...
this.button.click(function () { _this.buttonClick(); });
Related