Typescript error: An outer value of 'this' is shadowed by this container
Asked Answered
G

5

47

I had an error in a Typescript class method declaration, but I don't understand how the error message relates back to the bug.

The message seems to be saying that 'this' is of type any, but we are in a class definition, and so I thought 'this' was really clear.

Can someone please explain how the error message relates back to the bug?

Original method:

calcSize = function() {
    return this.width * this.length; // Error on this line
};

// Error text: 'this' implicitly has type 'any' because it does not 
//have a type annotation.ts(2683)
//app.ts(39, 16): An outer value of 'this' is shadowed by this container.

fix:

calcSize() {
    return this.width * this.length;
};

Full context (fixed):

class BaseObject {
    constructor(
        public width: number = 0,
        public length: number = 0
        ) {}

};

class Rectangle extends BaseObject {

    constructor(public width: number = 0, public length: number = 0) {
        super(width, length);
    }

    calcSize() {
        return this.width * this.length;
    };
}
Glarum answered 19/5, 2019 at 2:44 Comment(1)
Though this question is already answered, I will add that this error also occurs when the function calling for 'this' is mistakely defined outside the class, i.e after the class's closing curly brace.Dilate
I
70

In TypeScript (and ES6) exists two kinds of functions: The classic function declaration and the arrow function. Where the classic function declaration has the default floating binding logic for the this keyword - the arrow function will constantly use the value for this of the context containing the arrow function. In the example this will be the instance of the surrounding class.

class Rectangle extends BaseObject {
  // ..
  calcSize = function() {
    // The keyword "function" will cause "this" to be floating.
    // Since the function is explicitly assigned to calcSize
    // (older) TypeScript may not infer the type of "this".
    // The value of "this" can be re-bound by changing the context
    // using bind or call.
    // -> Value of "this" defaults to the class instance
    return this.width * this.length; // (potential) type Error on this line
  };

  calcSizeAsMember () {
    // This is also a classic function which will use floating binding
    // therefore "this" will be the type of the containing class.
    // The value of "this" can be re-bound by changing the context
    // using bind or call.
    // -> Value of "this" defaults to the class instance
    return this.width * this.length; 
  };

  calcSizeAsArrowFunction = () => {
    // This is an arrow function which has a constantly-bound "this" keyword, 
    // it is not possible to re-bind afterward.
    // The type of "this" is always the type of the containing class.
    // Changing the context using bind or call will have no effect
    // -> Value of "this" is always the class instance
    return this.width * this.length;
  };

};
Interracial answered 19/5, 2019 at 9:54 Comment(5)
It's important to point out that the standard method declaration is not bound to the class instance - eg if you grab a reference to it and call it this will be the global context. let ref = rect.calcSizeAsMember; ref(). This can be a common gotcha when working with async codePreciosa
Thanks for the hint. I've fixed it.Interracial
I always think of it the opposite. The classic "function" will NOT rebind, meaning it will use any "this" when it is called, and you could think of them as almost "floating". Where arrow () => functions will rebind, and always use the same "this" when they are called. An arrow function is equivalent to: function() { ... }.bind(someThis)Dramatics
Thank you for pointing that out. You are right, this is what I wanted to say. I edited my post to be more specific.Interracial
Arrow function worked like magicHong
H
10

After doing some research, I found a few interesting things. "this" can be a parameter of a regular function (not arrow function) and type can be either "any" or "unknown"

Notice the type "any" for "this" when declaring the function.

export async function main(this: any) {
    try {
        console.log(this);
    } catch (e: any) {

    }
}
Hong answered 31/3, 2022 at 1:35 Comment(0)
U
0

For anyone who explicitly wants to access class properties from a non class function. You could solve the problem like the following:

calcSize = function(this: Rectangle) {
    return this.width * this.length; // Error on this line
};

This will pass the object and is type safe.

Universalism answered 5/8, 2023 at 13:28 Comment(0)
N
0

Just throwing in my answer here. It looks like the typescript parser was updated. Previously this worked. But it looks like declaring @this after declaring optional parameters breaks the analyzer now.

/**
 * @param {object} [foo]
 * @param {string} [foo.bar]
 * @this {MyType}
 */
 function foobar({bar = 'default bar'} = {}) {...}

The above used to work, but the analyzer started complaining about it. It wasn't until I moved the @type declaration to the top of the jsdoc comment where it started working again (before the optional parameters)

Nicko answered 2/5 at 16:29 Comment(0)
K
-13

Do it with arrow function on setTimeout

setTimeout(() => {
  yourFunction()
}, 3000);
Knowle answered 23/10, 2020 at 12:41 Comment(1)
This won't solve the issue.Hong

© 2022 - 2024 — McMap. All rights reserved.