Search code examples
javascripttypescriptdurandalamd

Why does TypeScript need the "this." prefix for "internal" modules?


There are two ways to define modules in TypeScript:

"Singleton" lifestyle:

import app = require("durandal/app");
import ko = require("knockout");

export var name = ko.observable();

export function sayHello() {
    app.showMessage('Hello ' + name() + '! Nice to meet you.', 'Greetings');
}

"Transient" lifestyle:

import app = require("durandal/app");
import ko = require("knockout");

class AnythingYouLike {
    name = ko.observable();

    sayHello() {
        app.showMessage('Hello ' + this.name() + '! Nice to meet you.', 'Greetings');
    }
}

export = AnythingYouLike;

I use quotes for my names for these different ways to define a module as I can't work out what the "official" names are any more.

Using the "transient" style, particularly when using a framework like Durandal, makes good sense as you get better control over lifestyles of the viewmodel modules and avoid awkward bugs. One downside is the fact that you have to use "this" everywhere, which has two issues:

  1. Changing a module from one style to the other is tedious.
  2. Having this. sprinkled everywhere is noisy.

Why is it actually necessary to use this. at all, and why isn't it needed in both styles?


Solution

  • This isn't so much about modules, as it is about the fact that in the second "style," name is a member on the AnythingYouLike class, whereas in the first "style" you're declaring it as an independent variable in the global space.

    this makes sense in the former case when you realize that it's being used by a particular instance of that class to refer to it's own name - this.name, read: "my name".

    There is no such context in the first example, of course - there, you're just creating name.

    @War10ck is correct in pointing out that this is a very basic OOP thing.

    In response to your comment:

    But in the second example, there is context (the surrounding class declaration) that should mean this. can be implied, no?

    Java works that way (as do other OOP langs), and the semantics of this are more or less the same in TS and Java. But no, this cannot be implied in TS, it has to be explicit.

    Note that this is largely because of the fact that the "singleton style" exists in JS, and thus by extension in TS, whereas it does not in Java. To illustrate, consider what happens if we combine your examples:

    var name = "foo";
    
    class AnythingYouLike {
      name = "bar";
    
      sayHello() {
        app.showMessage('Hello ' + this.name); // shows: Hello bar
        app.showMessage('Hello ' + name); // shows: Hello foo
      }
    }
    

    name and this.name are both valid references here to two different values. There is no analogous construct to contend with in Java.