1

Closed

VS shows wrong 'this' value in debugger when using fat arrow functions

description

Consider the following code:
module MyModule {
    export class ItemData {
        public name: string = '';
        public drawbackFactor: number = 0;
        public baseMultiplier: number = 0;
        public actualMultiplier: (parent: Parent) => number;
    }

    export function ItemLoader(itemName: string, drawback: number, multiplier: number, target: ItemData) {
        target.baseMultiplier = multiplier;
        target.drawbackFactor = drawback;
        target.name = itemName;
        if (target.name == 'Foo') {
            target.actualMultiplier = (parent: Parent) => {
                var m: number = this.drawbackFactor; //break point here
                m = m / parent.skill;
                return m;
            }
        } else {
            target.actualMultiplier = (parent: Parent) => {
                return parent.skill;
            }
        }

    }

    export class ItemSlot {
        public baseItemData: ItemData = null;
    }

    export class Parent {
        public itemSlots: ItemSlot[] = [];
        public skill: number;
    }

    
}

var p = new MyModule.Parent;
p.skill = 10;
p.itemSlots.push(new MyModule.ItemSlot)
p.itemSlots.push(new MyModule.ItemSlot)
p.itemSlots.push(new MyModule.ItemSlot)

p.itemSlots[0].baseItemData = new MyModule.ItemData;
p.itemSlots[1].baseItemData = new MyModule.ItemData;
p.itemSlots[2].baseItemData = new MyModule.ItemData;

MyModule.ItemLoader('Bar', 10, 5, p.itemSlots[0].baseItemData);
MyModule.ItemLoader('Foo', 20, 15, p.itemSlots[1].baseItemData);
MyModule.ItemLoader('Bar', 30, 20, p.itemSlots[2].baseItemData);

console.log('Results:');
console.log(p.itemSlots[0].baseItemData.actualMultiplier(p));
console.log(p.itemSlots[1].baseItemData.actualMultiplier(p));
console.log(p.itemSlots[2].baseItemData.actualMultiplier(p));
console.log('End of Results.');
This will output the following to the console:
Results: ThisTest.ts:53
10 ThisTest.ts:54
NaN ThisTest.ts:55
10 ThisTest.ts:56
End of Results. 
The output shows NaN for p.itemSlots[1].baseItemData.actualMultiplier(p), when my small brain would have expected 2. I still sometimes get confused with how "this" works, but I was surprised to find out that Visual Studio shared my confusion in this instance:

Put a break point on the line labeled "//break point here". When the point is hit, hover the mouse pointer over the value of this.drawbackFactor. You will see that VS thinks that the value of this.drawbackFactor is 20. Then go to the immediate window and type "? this". You will see the following returned:
{...}
    [Methods]: {...}
    __proto__: {...}
    baseMultiplier: 15
    drawbackFactor: 20
    name: "Foo"
Clearly Visual Studio thinks that "this" points to "target". However, if you hit F10 to step to the next line (after the assignment), you will see that m is undefined still.

The fix to my code is to set m to target.drawbackFactor instead of this.drawbackFactor, but why is VS getting confused about what "this" is pointing to in this code? I can't think of a valid reason that the tooltip and the immediate window would think that this.drawbackFactor is 20 and then when you assign a variable to this.drawbackFactor, it remains undefined.

I apologize profusely if this is just my wild misunderstanding of "this".
Closed Nov 19, 2013 at 9:21 PM by RyanCavanaugh

comments

AdamFreidin wrote Sep 9, 2013 at 7:09 PM

Visual studio is confused by this in arrow functions. Arrow functions "bind" this to the scope of their declaration, overriding javascript's normal usage of this being bound to the object which the function is accessed from.

If you check the generated source you will see that typescript compiles "this" to "_this" which is bound to the module (rather than using .bind(this)).

You can use a regular function instead of => in this case to get normal/javascript this-binding.
            target.actualMultiplier = function(parent: Parent) {
                var m: number = this.drawbackFactor; //break point here
                m = m / parent.skill;
                return m;
            }

danquirk wrote Sep 9, 2013 at 8:16 PM

Yep, 'this' is confusing :)

Everything here appears to be working by design. When you hit the breakpoint you described you're inside the call 'p.itemSlots[1].baseItemData.actualMultiplier(p)'. Meaning when you enter actualMultiplier the value of 'this' is the instance that baseItemData points to, so an ItemData, which is what VS is showing.

But when you setup the actualMultiplier property in the MyModule.ItemLoader call the value of 'this' in ItemLoader was 'MyModule' which is what was captured into your function assigned to actualMultiplier (by virtue of using the fat arrow function, otherwise the new 'this' created by the function itself would've been the one captured). This is why you needed to use target.drawbackFactor, as target is the instance of ItemData that you actually wanted to access, where the 'this' value at that time was just the module instance.

The VS debugger doesn't handle this case very well so you see the misleading 'this' value where it just uses the most recently understood value. It doesn't understand the captured context from earlier unfortunately (there are more than a few ways you can use 'this' in interesting ways that confuse the VS tooling in this respect).

danquirk wrote Sep 9, 2013 at 8:16 PM

** Closed by danquirk 09/09/2013 1:16PM

danquirk wrote Sep 10, 2013 at 8:22 PM

Re-opening for tracking purposes so others can see it. Not likely to be fixed in the near term but it is a known issue.

giancarloa wrote Apr 10, 2014 at 10:51 PM

hello... is there any to vote for this issue? seems like an important issue that needs fixiing...

Clippy wrote Feb 11 at 2:27 AM

It's so very frustrating that this bug was closed in 2013 as Postponed, and as far as we can tell, that's the last time anyone at MS even paid attention to it.

Chrome's debugger handles this perfectly, but somehow Visual Studio still doesn't in 2017. How much further out can this get postponed? :(