Stavros_S
Stavros_S

Reputation: 2235

Undefined reference to 'this' in Typescript class

I'm having an issue with Typescript in which the this key word is showing up as undefined when debugging and, therefore, not calling some of my private class methods which are called from a callback method. My text editor seems to think that my reference is fine and is giving me intellensense, so I would assume the code is ok. below is a shortened example. The cacheUser and _processSuccessfulRegistration are not getting hit when the _handleCreationResponse callback method is called.

export class User extends Observable {
    public email: string;
    public password: string;
    public username: string

    constructor() {
        super()
        this.email = '';
        this.password = '';
        this.username = '';
    }

    public register() {
        let user = {
            // build out object
        }
        userService.createAccount(user)
            .catch(/* handle error */)
            .then(this._handleCreationResponse)
    }

    private _handleCreationResponse(response) {
        let status = response.status;
        let userData = response.result;

        switch (status) {
            case ApiStatus.Success:
                this._cacheUser(userData);
                this._processSuccessfulRegistration(userData);
                break;
            case ApiStatus.SomeError:
                // handle errors
                break;
            default:
                // do something else 
                break;
        }
    }

    private _cacheUser(user) {
        // handle user caching
    }

    private _processSuccessfulRegistration() {
        // call some other services and 
        // navigate to a new view
    }
}

Upvotes: 4

Views: 3402

Answers (1)

Nitzan Tomer
Nitzan Tomer

Reputation: 164307

When you pass functions as callbacks the functions are not bound to a specific this, so when they get executed this will refer to something else.

To avoid that you can either bind the function:

userService.createAccount(user)
            .catch(/* handle error */)
            .then(this._handleCreationResponse.bind(this)

Or use an arrow function which do save the right context for this:

userService.createAccount(user)
            .catch(/* handle error */)
            .then(response => this._handleCreationResponse(response))

Edit

There's another option which is to create this method as an arrow function to begin with:

private _handleCreationResponse = (response) => {
    let status = response.status;
    let userData = response.result;

    switch (status) {
        case ApiStatus.Success:
            this._cacheUser(userData);
            this._processSuccessfulRegistration(userData);
            break;
        case ApiStatus.SomeError:
            // handle errors
            break;
        default:
            // do something else 
            break;
    }
}

Then you can pass it as you did before and the context of this will be preserved.
If you use this option be aware that it's a bit different than what you had before as _handleCreationResponse won't be a method anymore but a function property of the instance (that is, it won't be part of the prototype).

Here's a short example to show the difference:

class MyClass {
    method1() {
        console.log("method 1");
    }

    method2 = () => {
        console.log("method 2");
    }
}

Compiles to:

var MyClass = (function () {
    function MyClass() {
        this.method2 = function () {
            console.log("method 2");
        };
    }
    MyClass.prototype.method1 = function () {
        console.log("method 1");
    };
    return MyClass;
}());

You cannot override methods which are arrow functions in extending classes, but as your method is private it shouldn't be a problem..

Upvotes: 5

Related Questions