Correct approaches for 'this'

Topics: General
Mar 26, 2013 at 8:46 AM
As I am using TypeScript I am running into the "this" issue more often.

Each time I hit this problem, I rewrite my functions as declarations, and then code the functions in the constructor using a lambda expression () =>. This then prompts the TypeScript compiler to create the _this reference and fix the binding.

The problem is that it seems my code is now trending toward a class with a massive constructor, containing all the code, and a bunch of declarations. Is this how it is supposed to be? Is this what others are finding?

Or is there a better way? Am I falling into an anti-pattern here?
Mar 26, 2013 at 10:12 AM
Curious to see some code to work out why this is happening for you. We've a massive TypeScript code base here (hundreds of methods across lots of classes) and have never had to do this once (aside from when handling the response of an event listener). Maybe this could be better answered with some code shown?
Mar 26, 2013 at 10:46 AM
A lot of my code is calling back to a webapi to get data, and also posting changes to the same. Each time I do this I have a callback mechanism, which changes scope, so requires a lambda method. If I try to structure my code in separate methods I end up writing more lambdas in the ctor..

Example:
   
  export class CallRateWithName {

        CallRateID = ko.observable(0);
        CallRateDefinitionID = ko.observable(0);
        RateName = ko.observable("");
        Rate = ko.observable();

        EditSave: () => void;

        // META for withholding data
        meta = function () { };


        SaveComplete: () => void;

        Editing = ko.observable(false);

        SetOriginal: ()=> void;

        RevertToOriginal: () => void;

        EditStart() { this.Editing(true); }
        EditCancel() {
            this.Editing(false);
            this.RevertToOriginal();
        }


        constructor(data: any) {
            AnvilJS.Utils.CopyProperties(data, this);

            this.SetOriginal = () => {
                (<any>this.meta).Original = ko.observable(this.Rate());
            };
            this.RevertToOriginal = () => {
                this.Rate((<any>this.meta).Original());
            };

            //save original rate
            this.SetOriginal();

            this.SaveComplete = () => { 
                alert("saved");
                this.Editing(false);
            };

            this.EditSave = () => {
                //alert("Sorry, not implemented");
                var data = ko.toJS(this);
                delete data.EditSave;
                AnvilJS.api.CustomerRates.ChangeCustomerRate(data,
                    this.SaveComplete,
                    (error) => {
                        this.EditCancel();
                        alert(error);
                        //throw error;
                    });
            }

        }
In plan JS one generally creates a _this or a self variable and reference this within the functions to ensure the correct scope. Except you can't (that I know of) do this in typescript since all variables have to be accessed via 'this'.
Mar 27, 2013 at 10:36 PM
Edited Mar 27, 2013 at 10:38 PM
@Quango

That code is sure far from what you would do normally.
I am guessing you have an API that calls your methods from outside but from a different context so 'this' is broken?

What you can do instead is:
external class MyClass {
    public SaveComplete;

    constructor() {
        this.SaveComplete = () => this.InternalSaveComplete();
    }

    private InternalSaveComplete() {
        // this now refer to our class as we changed scope above.
        // so you can place the implementation here.
    }
}
Example of different bindings
Mar 28, 2013 at 8:54 AM
Ah! that sounds like a much better approach: I can still keep the code outside the constructor, but just 'link' in this manner to ensure the correct context is passed.

many thanks
Mar 28, 2013 at 4:26 PM
I've wondered if a second "this" could be added that would work more like the standard this from other languages. TypeScript could have a property operator @ that would be bound to the class, rather than whatever context the function is attached to at runtime. I wonder if that duality would get confusing though.

To illustrate:
class MyClass {
    public myVar;

    constructor() {
        this.myVar = "someVal";
    }

    public myMethod() {
        console.log( @myVar ); // outputs "someVal"
        console.log( this.myVar ); // outputs "someVal" if called from within MyClass context
    }
}
var myObj = new MyClass();

// to simulate what often happens with async handlers
var randomObj = {
    myVar: "someOtherVal",
    borrowedMethod: myObj.myMethod
};

randomObj.borrowedMethod();
// outputs:
//   "someVal"
//   "someOtherVal"

Mar 28, 2013 at 5:41 PM
Edited Mar 28, 2013 at 5:42 PM
Yes, I agree that we need something along these lines.

The standard JavaScript pattern for this is to define a local var "self" or "_this" var and reference that to ensure the correct scope. Sadly this isn't possible in TypeScript classes as there is no concept of a "class-scope" variable. "this" is only guaranteed in the c'tor and can't be "saved".

What will tend to confuse people (me included) is that if you use "this" in TypeScript code, Intellisense will 'type' it as the class: except of course we know there is no guarantee of this when the function is called.

I'm not blaming TypeScript per se, I think it's a flaw in the ECMAscript 6 design; but TypeScript could perhaps fix it.

My personal preference for the accessor would be something like @this or even @self (to avoid confusion) - e.g. console.log(@self.myVar) - a reserved word that is guaranteed to be the scope of the class in which it is referenced.

[edit] I am going to log as an issue - cannot see an existing one
Mar 28, 2013 at 6:37 PM
I have now posted this as Issue 851

Just tried to see if I could create a workaround by declaring _this (which we know is used in the constructor) and referencing it from within the code. It didn't work because the 'var _this = this' line that TypeScript generates is local to the constructor function, and not the function closure.
// try sneaky access to '_this'
declare var _this:Greeter;

class Greeter {
    greeting: string;
    greet() {
        return "Hello, " + _this.greeting;
    }
    
    dummy: ()=>void;
    
    constructor(message: string) {
        this.greeting = message;
        this.dummy = ()=> { var tmp = this;}
    }
}

var greeter = new Greeter("world");

var button = document.createElement('button');
button.innerText = "Say Hello!";
button.onclick = function() {
    alert(greeter.greet());
}

document.body.appendChild(button);

Mar 28, 2013 at 7:49 PM
When you need a class method to be used as a callback, you can either wrap it in a lambda specifically for the callback, or use this.f.bind(this).

e.g.
class Greeter {
    name: string;
    constructor() {
        $.get("/username").success(this.gotUsername.bind(this));
        // or
        $.get("/username").success((name) => { this.gotUsername(name); });
    }
    gotUsername(name: string) {
        this.name = name;
    }
}
As I understand it, the second form with the lambda gives better performance.

If you're not passing them as callback functions, you shouldn't need to do anything odd at all.
Mar 30, 2013 at 12:14 AM
Edited Mar 30, 2013 at 12:22 AM
The lambda makes things easier, but wouldn't a class property accessor make things even easier? Also, as a matter of personal preference, I like the plain @propname, vs. @self.propname better - less typing.

This could conceivably be only used for call time reference passing:
class Greeter {
    name: string;
    constructor() {
        // So anywhere this is used
        $.get("/username").success(@gotUsername);
        // would get turned into this (or the bind method)
        $.get("/username").success((name) => { this.gotUsername(name); });
    }
    gotUsername(name: string) {
        this.name = name;
    }
}
Or if it's property based:
class Greeter {
    name: string;
    constructor() {
        $.get("/username").success(@gotUsername);
    }
    gotUsername(name: string) {
        @name = name;
    }
}
Methods that contain the use of that operator would have to be compiled differently to protect the reference, which could be difficult. Or, the TS compiler could detect when it's used in both places (like above) and swap out the passing reference (inside success in this example) with a lambda, and the @ in the function body with a simple this. I'm not sure how flaky that would be though.

Edit: I guess that would only work in cases where you are using an internal method. The reference problem would still exist in cases where you were subscribing to a method on another object.