An introduction to TypeScript’s module system

The module system is an interesting feature of TypeScript, the statically typed superset of JavaScript.
Modules provide the possibility to group related logic, encapsulate it, structure your code and prevent pollution of the global namespace.
Modules can provide functionality that is only visible inside the module, and they can provide functionality that is visible from the outside using the export keyword.
TypeScript’s module system comes in two flavors: internal and external modules.
In this blogpost I’m going to explain how to provide and consume both internal and external TypeScript modules.

A comparison of internal and external modules

Internal modules are TypeScript’s own approach to modularize your code. Internal modules can span across multiple files, effectively creating a namespace.
In the generated JavaScript, internal modules will materealize as implementations of the module design pattern.
There is no runtime module loading mechanism, so – in a browser environment – you have to load the modules using <script/> tags on your own. Alternatively, you can compile all TypeScript files into one big JavaScript file that you include using a single <script/> tag.

External modules leverage a runtime module loading mechanism. You have the choice between CommonJS and AMD.
CommonJS is used by node.js, whereas RequireJS is a prominent implementation of AMD often used in browser environments.
When using external modules, files become modules. The modules can be structured using folders and sub folders.

Working with internal modules

How to provide internal modules

To create an internal module, we use the module keyword with a module name.
Classes, interfaces, functions and variables in the module’s body will be visible to the outside of the module if you put an export keyword right in front of them.

module mymod {

  export function doSomething() {
    // this function can be accessed from outside the module
  }

  export class ExportedClass {
    // this class can be accessed from outside the module
  }

  class AnotherClass {
    // this class can only be accessed from inside the module
  }
}

For nesting modules, you can either use a dot notation for the module name

module mymod.submod {
  // ...
}

or you can nest module statements

module mymod {
  module submod {
    // ...
  }
}

How to consume internal modules

You can either use classes from internal modules by addressing them using their fully qualified name, or using an import statement.
Here is an example that shows how to use classes from a module by addressing them with their fully qualified name:

var exportedClassInstance = new mymod.ExportedClass();

Here is another example that uses the import keyword:

import ExportedClass = mymod.ExportedClass;
var exportedClassInstance = new ExportedClass();

In fact, this simply creates an alias for the fully qualified class. You could even use another name for the imported class, as shown below.

import ExpCl = mymod.ExportedClass;
var exportedClassInstance = new ExpCl();

Working with external modules

How to provide external modules

If you want to use external modules, you have to decide which module system (AMD or CommonJS) to use and then compile your sources using the --module compiler flag. Possible values are amd or commonjs.

You don’t have to use the module keyword in external modules, as the file’s name and path will create the namespace.

Again, classes, interfaces, functions and variables with the export keyword in front of them will be visible to the outside of the module.

export function doSomething() {
  // this function can be accessed from outside the module
}

export class ExportedClass {
  // this class can be accessed from outside the module
}

class AnotherClass {
  // this class can only be accessed from inside the module
}

If you go with the one class per file best practice, it’s recommended to use a single export= statement.
This makes consuming these external modules easier (see How to consume external modules).
The export= statement turns the exported object into the module.

class ExportedClass {
  // this class can be accessed from outside the module
}

export = ExportedClass;

How to consume external modules

If you have used the export= statement in the exported module, you can use the external module as shown below.

import ExportedClass = require("mymod/ExportedClass");
var exportedClassInstance = new ExportedClass();

Please note that you call require() with the filename of the module (and optionally with a path).

If you have not used the export= statement, e.g. because you put multiple external classes into one file, you would import the module (in this case a file named “mymod.ts”) and have to access the class of the module via a dot notation as shown below.

import mymod = require("mymod");
var exportedClassInstance = new mymod.ExportedClass();

How to consume external JavaScript libraries using the require keyword

In general, the require(FILENAME) call will look for a TypeScript file called FILENAME.
If you want to use a JavaScript library using e.g. RequireJS, you have to give the compiler a hint that it doesn’t have to look for a TypeScript file on the filesystem.
You do this with the declare module statement. Here is an example from the underscore declaration file from DefinitelyTyped.

declare var _: UnderscoreStatic;
declare module "underscore" {
  export = _;
}

Now you can consume the library with a require statement.

import _ = require("underscore");
Short URL for this post: http://blog.oio.de/LNX6G
This entry was posted in Web as a Platform and tagged , , , , , . Bookmark the permalink.

Leave a Reply