Merge import modules

Topics: General, Language Specification
Jul 12, 2014 at 9:31 PM
Edited Jul 12, 2014 at 9:33 PM
I'm struggling with large-scale TypeScript development. Imagine I had two animals, and each contained a lot of code.
// zebra.ts
export class Zebra {
    constructor() {
        console.log("You made a zebra. Good for you!");
    }

    // lots of code goes here (1000ish lines)
}

// lion.ts
export class Lion {
    constructor() {
        console.log("You made a lion. Good for you!");
    }

    // lots of code goes here (1000ish lines)
}
I definitely want those large classes to be in separate files.

When I want to import them, I need to do something like this:
// zoo.ts
import ZebraModule = require("Zebra");
import LionModule = require("Lion"); 
This seems really silly. We loose the benefits of compartmentalizing the imports into one container. There's no IntelliSense friendly way of seeing all the imported animals.

Since both variables are of type module, it makes sense that module merging (http://www.typescriptlang.org/Handbook#declaration-merging-merging-modules) would somehow be an option:
// zoo.ts (this doesn't work, but it would be awesome if it did)
import Animals = require("Zebra");
import Animals = require("Lion"); 
Am I missing something that allows for this already?
Jul 14, 2014 at 7:58 AM
This is already possible, if you change your code slightly:
// Zebra.ts
class Zebra {
  /* ... */
}
export = Zebra;

// Lion.ts
class Lion {
    /* ... */
}
export = Lion;

// zoo.ts
export import Zebra = require("Zebra"); Zebra;
export import Lion = require("Lion"); Lion;
Here, 'export' has been moved from the class definition to a statement, to remove one unnecessary level of indirection (only works if it's just the class you want to export, not if you want to e.g. export multiple classes/interfaces from one file).
Also note the 'export import' syntax to re-export existing things.

Note that TypeScript currently doesn't emit code for the imports in zoo.ts, because it apparently thinks they are not really used in the file. I think that's a bug. As a work-around, I've placed a no-op statement after the imports to convince the compiler to include the export statements.
Jul 14, 2014 at 8:10 AM
For easy reference, here's an existing bug-report for the unused-module-problem: https://typescript.codeplex.com/workitem/2304
Developer
Jul 14, 2014 at 7:53 PM
I suspect the ideal solution for you is merged external modules which we don't support yet. The internal module version would be like:
file1.ts:
module Animals {
    export class Zebra {
        // stuff a Zebra does
    }
}

file2.ts:
module Animals {
    export class Lion {
        // stuff a Lion does
    }
}

file3.ts:
/// <reference path='file1.ts'/>
/// <reference path='file2.ts'/>

var anAnimal = new Animals. // now you get Intellisense with Lion and Zebra available
If file1 and file2 could export the Animals module then you'd be able to write file3 using import/require rather than only /// references. Unfortunately we've had trouble designing a satisfactory user experience here, suffice to say it has definitely been on our radar for a long time. If you must use import/require and you want the classes under a single namespace then yeah for the moment you're stuck with having to put them in the same file that is exporting that module. The use of export= shown above does help in other ways too.