dotsa
dotsa

Reputation: 941

TypeScript - 'this' defaults to 'any'

Can some one please explain why i do not get intellisense in 'setItem' function for 'this' variable. My understanding it should be scoped to '_storageAdapter' property?

class TestClass {
private testOne = () => {
}

_storageAdapter: {
    toType: (obj: any) => string,
    getItem: (key: string) => void,
    setItem: (key: string, value: string) => void,
}
= {
    toType: (obj: any) => {
        return "hello";
    },
    getItem: (key: string) => {
        this.testOne() // this is class scoped;
    },
    setItem: function (key: string, value: any) {
        this.toType(value); // this should be property scoped? but no intellisense? its just set to 'any'
    }
}

}

Upvotes: 1

Views: 78

Answers (2)

basarat
basarat

Reputation: 276057

Simplified sample:

class TestClass {
    private testOne = () => {
    }

    _storageAdapter: {
        getItem: (key: string) => void,
        setItem: (key: string, value: string) => void,
    }
    = {
        getItem: (key: string) => {
            this.testOne() // this is class scoped;
        },
        setItem: function (key: string, value: any) {
            this.somethingThatDoesNotExist(); // this should be property scoped? but no intellisense? its just set to 'any'
        }
    }
}

The reason is that function does not capture this and it will really depend on the user of the function how they call it.

An arrow function captures this so typescript has stronger guarantee.

More

Some flags that will prevent you getting caught off guard: noImplicitAny https://basarat.gitbooks.io/typescript/content/docs/options/noImplicitAny.html

Upvotes: 1

Nitzan Tomer
Nitzan Tomer

Reputation: 164237

Regular js functions don't save the scope of this, for example:

class A {
    obj = {
        withoutScope: function() {
            console.log(this);
        },

        withScope: () => {
            console.log(this);
        }
    }
}

Compiles into:

var A = (function () {
    function A() {
        var _this = this;
        this.obj = {
            withoutScope: function () {
                console.log(this);
            },
            withScope: function () {
                console.log(_this);
            }
        };
    }
    return A;
}());

Notice the difference between the two console.log: the withoutScope has this while withScope has _this (which is defined above as var _this = this).
This is how the typescript compiler translates the arrow function, but if you compile it with ES6 target then it will keep it as an arrow function without using the _this = this trick, but the result will be the same.

If you call those methods you get:

let a = new A();
a.obj.withoutScope(); // Object {}
a.obj.withScope(); // A { obj: Object }

(code in playground)

The same happens in your example, the first two functions are arrow functions which keep the right scope of this, but the third doesn't.
You can either use an arrow function there as well or you can use the Function.prototype.bind function:

setItem: function (key: string, value: any) {
    this.toType(value); // this should be property scoped? but no intellisense? its just set to 'any'
}.bind(this)

But it will probably only help you at runtime, as I'm not sure that your intellisense will get that.

Upvotes: 2

Related Questions