How to work with closures between multiple files

Topics: General, Language Specification
Jul 18, 2013 at 3:18 PM
Edited Jul 18, 2013 at 5:19 PM
I'm doing an experiment porting Backbone to TypeScript just to see how it would go: https://github.com/nvivo/backbone-ts/blob/bf23446710e586bf7870cfdebdc008455b481762/src/Backbone.ts

One thing I'd like to do is to split the code into multiple files, but it gets to a problem where the main scope has a lot of methods, aliases and variables that are accessed through closures everywhere in the code.

Because of this, I cannot break the code into multiple files without moving them to some place that can be accessed from other files, like an "config" or "utility" module.

I could compile the files individually first, then merge them in the right places using an external builder like grunt, but this would make most of typescript type checks useless, as I would need a lot of "declare" statements at the top of each file to compensate for things not being where they should during development.

Since JavaScript has no concept of internal classes or protected properties, we need to use closures to avoid exposing some internals. But since Typescript has no way to merge code into the same scope, I'm finding that either we have everything public or we have everything in the same file.

So, are there any plans for 1.0 or after that to support compiling multiple files to the same scope, similar to how partial classes work in C#? Something like:

File1.ts
partial module Foo {
    var x = 1;
}
File2.ts:
partial module Foo {
    function doSomething() {
        int count = x;
    }
} 
This would get merge to something like this during compilation:

File1:
module Foo {
    var x = 1;
    function doSomething() {
        int count = x;
    }
}
It would be nice to see how the developers envision this kind of scenarios, if something will be baked into the language or if the idea is to have this into the build process.

Also, please le me know if I'm overcomplicating this. If there is a better solution already, please let me know.
Developer
Jul 19, 2013 at 11:09 PM
Internal module declarations already merge when multiple declarations are included in the same compilation (see 10.5 Declaration Merging in the language specification). Likewise for interfaces (see 7.2). Merging external modules presents some additional challenges which we have discussed but are not currently supported.
Jul 19, 2013 at 11:32 PM
Thankd Dan.

I'm talking about merging the actual code, not declarations.

This is just an idea, but I think it would be good to take closures between files into consideration after 1.0 somehow.
Developer
Jul 22, 2013 at 8:20 PM
The actual code is merged, you just need to do a little more work to handle circularity like this. For example:

a.ts:
module Foo {
    export var x = 1;
}
b.ts:
declare module Foo { // alternatively this could be a referenced .d.ts for a.ts
    var x: number; // this is necessary for the compiler to not complain about Foo.x below
}

module Foo {
    export function doSomething() {
        var count = Foo.x;
    }
} 
tsc a.ts b.ts --out m.js results in this m.js:
var Foo;
(function (Foo) {
    Foo.x = 1;
})(Foo || (Foo = {}));
var Foo;
(function (Foo) {
    function doSomething() {
        var count = Foo.x;
    }
    Foo.doSomething = doSomething;
})(Foo || (Foo = {}));
Note that the order of emit is based on the command line order of files so you need to manage circularity yourself. Now you'd want a .d.ts file for the combined module definition so that another file could say /// <reference path='m.d.ts'/> and get the full strongly typed module definition, and for a concat tool to merge m.js with your file that references its implementation code.

Obviously there are a few extra steps that you have to deal with but it does work. We have talked in the past about things like a 'partial' keyword. If you want to file a Codeplex suggestion for any particular scenarios in this area feel free.
Jul 22, 2013 at 9:55 PM
Edited Jul 22, 2013 at 9:55 PM
Hi Dan,

I'm talking about really merging the files, nothing to do with the declarations.

In order to make this work as closures, this would need to be built into the same scope:
var Foo;
(function (Foo) {
    // contents of file 1
    var x = 1;

    // contents of file 2
    function doSomething() {
        var count = x;
    }
    Foo.doSomething = doSomething;
})(Foo || (Foo = {}));
I'm asking if the development team have a plan to map the closure pattern between files to typescript.

I have been porting some code to TypeScript, but in order to make functions accessible to other files, I had to move them from closures to modules and make them "visible" to the outside world as you did. Sometimes this is not desirable, as closures are the only way to have real "private" members in javascript.

The current solution I found was to create the closures in a different "project", declare them again like you did in a .d.ts, and compile the 2 sets of files independently, then use a tool like grunt to merge the files and add an outer closure, so the code would look like:
(function() {
   // contents of helper methods and variables accessed through closures
   // the rest of the project merged after compilation
})();
This would also be needed to create something like "noConflict" that almost every library uses.

So, is there a way or is there a plan to support this in the future, or the direction would be to use external tools to create libraries like we have today in javascript?
Developer
Jul 24, 2013 at 12:00 AM
Ah, I see, you want to share privates/non-exported members. I suspect plans to address this particular scenario in a more seamless way in the future may be predicated on how ECMAScript 6 ends up handling private names and any potential alignment with that.