1

How would you recommend I convert this JavaScript to TypeScript?

JAVASCRIPT:

function MyClass() {
    var self = this,
        var1 = "abc",
        var2 = "xyz";

    // Public
    self.method1 = function () {
       return "something " + self.var1;
    };

    self.method2 = function (parm1) {
        var x = self.method1();
        return x + " something";
    };

    // Private 
    function func1(parm1, parm2) {
        return parm1 + parm2;
    }
}

This is what I would like to do in TypeScript, or at least how I would expect to write it:

module MyNamespace {
    export class MyClass {

        private var1: string = "abc";
        private var2: string = "xyz";

        constructor() {
        }

        // Public
        public method1() {
            return "something " + self.var1;
        }
        public method2(parm1) {
            var x = this.method1();
            return x + " something";
        }

        // Private 
        private func1(parm1, parm2) {
            return parm1 + parm2;
        }
    }
}

But the generated JavaScript puts everything on the prototype and I will have some "this" scoping issues:

var MyNamespace;
(function (MyNamespace) {
    var MyClass = (function () {
        function MyClass() {
            this.var1 = "abc";
            this.var2 = "xyz";
        }
        // Public
        MyClass.prototype.method1 = function () {
            return "something " + this.var1;
        };
        MyClass.prototype.method2 = function (parm1) {
            var x = this.method1();
            return x + " something";
        };

        // Private
        MyClass.prototype.func1 = function (parm1, parm2) {
            return parm1 + parm2;
        };
        return MyClass;
    })();
    MyNamespace.MyClass = MyClass;
})(MyNamespace || (MyNamespace = {}))

I know I can declare the methods within the constructor, use lambda functions to get "_this" instead of "this", etc. but what are the best practices of "porting to TypeScript"?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
user210757
  • 6,996
  • 17
  • 66
  • 115
  • As some users have hinted; the answer to the question "How would you recommend I convert this JavaScript to TypeScript?" is: You don't. Try to embrace the way JavaScript works instead of fighting it as your code will work better and consume less cpu and memory. After creating quite some projects with JS I have yet to find a use for creating "privates" through closures (instead of just naming it `_private`) but maybe you know something I don't and I would gladly learn of a situation where it was needed. – HMR Nov 08 '13 at 02:56
  • Yes I agree but it is my task – user210757 Nov 08 '13 at 15:33

3 Answers3

4

But the generated Javascript puts everything on the prototype

I think that's what you want to happen. The only time it creates a "this" issue is when you want to pass the function as a callback. That's a known concern when passing callbacks and when doing so is the right time to worry about it. To me it feels like the JavaScript way and it's better to embrace the system rather than fight it.

If every instance has a unique function closure you're using a lot more memory or at a minimum creating way more work for the optimizer.

Jeffery Grajkowski
  • 3,982
  • 20
  • 23
3

I think the porting you have there looks right. You should consider letting class methods stay on the prototype for memory, performance, and reusability reasons. But if you have some methods you really want to be on the instance for the purposes of keeping this, you can use the member initialization + lambda syntax:

class MyClass {
    greeting = 'hello, ';

    /** someFunc is safe to use in callback positions */
    someFunc = (msg: string) => {
        console.log(this.greeting + msg);
    }
}
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • 1
    If you understand that `this` is resolved when invoking the function and you know that you can pass a closure instead of the function to timeouts or event handlers there should not be a problem resolving `this`. Unless the person who wrote the code that registers event handlers/sets timeout does not understand how JavaScript works. To solve this shortcoming by creating another one (not using prototype) does not seem the best way. You've mentioned this in your answer but I feel the need that this needs to be stressed a bit more for future visitors. – HMR Nov 08 '13 at 02:52
  • 1
    +1 to that. I definitely agree in principle, but in the case of a class that is going to have many different call sites all setting up callbacks on the same method, it's more efficient to have the class centralize the this-capturing closure rather than have every caller create a new closure for that purpose. Whether that gain outweighs the challenges in effectively documenting and enforcing that behavior is probably project-specific (or even class-specific). – Ryan Cavanaugh Nov 08 '13 at 04:47
  • I have provided a centralized example in my answer, it has finer control over what's going to be in the scope of the closure. It should still be more efficient since the other option would create closure(s) on instance creation that may or may not be used. – HMR Nov 08 '13 at 05:02
  • I get this error: `Keyword this cannot be referenced in initializers in the class body, or in super constructor calls.` – ctrl-alt-delor Dec 09 '13 at 13:13
3

JavaScript determines the value of this when calling/invoking the method, not when you declare it. https://stackoverflow.com/a/16063711/1641941 (under "The this variable")

To set the right context you can use =>

class MyClass {
    greeting = 'hello, ';
    someFunc(msg: string) {
        console.log(this.greeting + msg);
    }
}

var m = new MyClass();
setTimeout(() => {m.someFunc("hello")},100);

setTimeout will be passed a closure that is created in the current scope.

If you were to set an interval or event handler and the current scope has a very large irrelevant variable(s) in it then it's better to declare a function that returns the closure somewhere outside of the current scope and pass it the only the variables that are relevant for this closure.

Code that will return a closure function for you and limit scope:

class MyClass {
    greeting = 'hello, ';
    someFunc(msg: string) {
        console.log(this.greeting + msg);
    }
    public static closures ={
        someFunc(me:MyClass,msg:string){
            return function(){
                me.someFunc(msg);
            };
        }
    }
}

var m = new MyClass();
setTimeout(MyClass.closures.someFunc(m,"hello"),100);
Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160