Typescript idiom to prevent duplicate modules included in a page

Topics: General
Apr 13, 2014 at 7:47 AM
Suppose I have a TypeScript module defined like:
module Foo {
    export var now = new Date();
}
This gets transpiled to:
var Foo;
(function (Foo) {
    Foo.now = new Date();
})(Foo || (Foo = {}));
If I were writing in pure JavaScript, I would have included something like:
var Foo;
(function (Foo) {
    if (window.Foo) return; // <-- PREVENT DUPLICATES!
    Foo.now = new Date();
})(Foo || (Foo = {}));
to ensure that this module gets defined only once even if the <script/> gets included multiple times in a page. How do I achieve the same effect in TypeScript?

(cross-posted at http://stackoverflow.com/questions/23005316)
Apr 14, 2014 at 7:27 AM
Edited Apr 14, 2014 at 7:32 AM
You can't define duplicate modules. They all get merged into one.
module Foo {
    export var now = new Date();
}

module Foo {
    export var now = new Date();
}
doesn't compile because the exported property "now" is duplicated, but if it did compile, you'd end up with this:
var Foo;
(function (Foo) {
    Foo.now = new Date();
})(Foo || (Foo = {}));

var Foo;
(function (Foo) {
    Foo.now = new Date();
})(Foo || (Foo = {}));
which is why it doesn't compile. Since modules specified more than once all merge, you cannot simply jump out of them.

Now, that said, I have just requested [again] more control over classes and modules (https://typescript.codeplex.com/workitem/2432), but I guess we'll see if it gets shot down or not (a similar request I made was rejected; not sure why - perhaps I didn't explain it well). I think this could be a powerful feature for more powerful dynamic scripts.
Apr 14, 2014 at 2:30 PM
Edited Apr 14, 2014 at 2:31 PM
Some more details of my situation to motivate this requirement better:

I am writing a library in TS. I deliver this library as the compiled JS. The user of this library is expected to include the compiled JS into her page with the <script/> tag. My library is allowed to introduce only a single new name into the global namespace - say Foo, in the example above. Hence all my TS code is organized in the Foo module. But I would like to have this module compile to JS such that it prevents (accidental) duplicate inclusions of itself in the page.
Apr 14, 2014 at 8:17 PM
Probably a better way to do this is to load the script dynamically, for example:
// loader.js 

if(!window.Foo) {
      
    var tag = document.createElement('script'); 
    tag.type = 'text/javascript';
    tag.src = 'http://foo.com/foo.js'; // This is your actual script
    var first = document.getElementsByTagName('script')[0]; 
     first.parentNode.insertBefore(tag, first);
}

Basically, the user of the library will include loader.js, which checks and loads the actual script if necessary.
Apr 14, 2014 at 8:34 PM
@nabog, thanks!

The approach your outlined works in general, but not in my specific case. I want my JS script to be included in the page early (right after the <HEAD/>) and I want the browser to execute it inline (it basically records to the time of the page load in Foo.now variable).
Apr 14, 2014 at 11:49 PM
Well, it would seem this works now:
module _ {
    if (_.A) return;

    export module A {
        export class B {
        }
    }
}
http://goo.gl/k9eo31

Interesting. :/
Apr 16, 2014 at 12:05 AM
@jamesnw, your code gave me this idea:
module window {
    if (window.Foo) return;

    export module Foo {
        export var now = new Date()
    }
}
This compiles to:
var window;
(function (window) {
    if (window.Foo)
        return;

    (function (Foo) {
        Foo.now = new Date();
    })(window.Foo || (window.Foo = {}));
    var Foo = window.Foo;
})(window || (window = {}));
which is what I want! Thanks!
Apr 16, 2014 at 2:35 AM
Cool beans. I figured you could probably jimmy-rig it to fit. ;)