- In TypeScript you can use modules to organize your code, avoid polluting the global space, and expose functionalities for others to use.
- Multiple modules can be defined in the same file. However, it makes more sense to keep on module per file
- If you want, you can split a single module across multiple files
- If you decide to split a module across different files, this is how you would do it:
- Create the module file:
mymodule.ts
and declare your module there:module MyModule {}
- Create another file:
mymodule.ext1.ts
and on top of the file add:/// <reference path="mymodule.ts" />
. Then in the file, you can use the same name of the module and add more stuff to it:module MyModule { // other stuff... }
- Then in your main file, you need two things on top of the file:
/// <reference path="mymodule.ts" />
/// <reference path="mymodule.ext1.ts" />
- Then, you can use the name of your module to refer to the symbols defined:
MyModule.something
,MyModule.somethingElse
- Create the module file:
- TypeScript has two system: one used internally and the other used externally
- External modules are used if your app uses CommonJS or AMD modules. Otherwise, you can use TypeScript's internal module system
- Using TypeScript's internal module system, you can:
- use the
module
keyword to define a module:module MyModule { ... }
- split modules into different files that contribute to a single module
- use the
/// <reference path="File.ts" />
tag to tell the compiler how files are related to each other when modules are split across files
- use the
- Using TypeScript's external module system:
- you cannot use the
module
keyword. Themodule
keyword is used only by the internal module system. - instead of the
reference
tag, you can use theimport
keyword to define the relationship between modules - you can import symbols using the file name:
import mymodule = require('mymodule')
- you cannot use the
The project files for this chapter are in angular2-intro/project-files/typescript/modules
.
Let's create a simple module that contains two classes. The first class is a vehicle class and the second is a car class that inherits from the vehicle class. Then we are going to expose the car class to the outside world and import it from another file. The project files for this section are in angular2-intro/project-files/typescript/modules/basic-module
.
First, create the main.ts
file and copy paste the following:
main.ts
module MyModule {
class Vehicle {
constructor (public name: string = 'Vehicle', private _distance: number = 0) {}
get distance():number { return this._distance; }
set distance(newDistance: number) { this._distance = newDistance; }
move() { this.distance += 1 }
}
}
- On line 1 we are defining the module called
MyModule
. - Inside this module we have defined a class called
Vehicle
that has a distance property and a setter and getter.
Now we want to create a class and export it so that it can be imported by others:
main.ts
module MyModule {
class Vehicle {
constructor (public name: string = 'Vehicle', private _distance: number = 0) {}
get distance():number { return this._distance; }
set distance(newDistance: number) { this._distance = newDistance; }
move() { this.distance += 1 }
}
// -> adding the car class
export class Car extends Vehicle {
constructor (public name: string = 'Car') {
super();
}
}
}
- On line 9 we are using the
export
keyword to indicate that theCar
class is exposed and can be used by others.
Now, let's create a car using the Car
class defined in the MyModule
module:
const mycar = new MyModule.Car('My Car');
console.log(mycar.name);
Note that we accessed the Car
class using the MyModule
symbol: MyModule.Car
. Now we can split up the module into its own file and import it into the main file. Let's create a file called MyModule.ts
and move the module definition to that file. Now in our main file we are just going to import the module and use the car class from it.
main.ts
/// <reference path="MyModule.ts" />
const mycar = new MyModule.Car('My Car');
console.log(mycar.name);
Note that we can create an alias to the MyModule
using import AliasName = MyModule
. Now you can reference the module name with AliasName
:
/// <reference path="MyModule.ts" />
import AliasName = MyModule;
const mycar = new AliasName.Car('My Car');
console.log(mycar.name);
Now if we run this in debug mode, the compiler will complain that it can't find the MyModule
reference. Because of that we need to make some changes to our config files. First, we are going to add the out
property in the tsconfig.json
file. This will tell the compiler to compile all the files into a single file:
"out": "output/run.js",
So our tsconfig.json
file will look like this:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"outDir": "output",
"out": "output/run.js",
"watch": true
}
}
Now if you run the build, you should see that all the project has been compiled into output/run.js
. In addition to the tsconfig.json
file, we are going to update the launch.json
file and add a new configuration field:
{
"name": "TS All Debugger",
"type": "node",
"program": "output/run.js",
"stopOnEntry": false,
"sourceMaps": true
}
Now we should be able to use the debugger and put breakpoints in our TypeScript files. Select TS All Debugger
from the debugger dropdown and run the debugger and it should stop if you put a breakpoint in any of your TypeScript files.
NOTE Using the configuration files above we can compile all the TypeScript files into a single JavaScript file. But sometimes that is not what you want. Be aware that using the above configuration you will not get an output for each TypeScript file.
Internal modules in TypeScript are open ended. This means that you can define a module with the same name in different files and keep adding to it. This is also known as merging. In this section we are going to demonstrate merging multiple files that contribute to a single module called Merged
. The project files for this section are in angular2-intro/project-files/typescript/modules/merged-module
.
First, we are going to make two files: A.ts
and B.ts
. In each file we are going to define the Merged
module:
// A.ts
module Merged {
const name = 'File A'; // not exported
export class Door {
constructor (private _color = 'white') {}
get color() { return this._color; }
set color(newColor) { this._color = newColor; }
}
}
and then the B.ts
file:
// B.ts
module Merged {
const name = 'File B'; // not exported
export class Car {
constructor(public distance = 0) {}
move () {this.distance += 1;}
}
}
We just created two files called A.ts
and B.ts
and each file we defined the Merged
module and added a class to each and exported it. Now we are going to make the main.ts
file and reference these two files:
// main.ts
/// <reference path="./A.ts" />
/// <reference path="./B.ts" />
And now we can use the classes defined in the Merged
module, that is the Car
and the Door
class:
/// <reference path="./A.ts" />
/// <reference path="./B.ts" />
const car: Merged.Car = new Merged.Car();
const door: Merged.Door = new Merged.Door();
door.color = 'blue';
car.move();
car.move();
console.log(car.distance);
console.log(door.color);
if you run the build task (command + shift + b) and hit F5 you should see the following output:
node --debug-brk=19237 --nolazy output/run.js
Debugger listening on port 19237
2
blue
In addition to TypeScript's internal module system, you can use external modules as well. In this section we are going to demonstrate how you can use external modules in TypeScript. The project files for this section are in angular2-intro/project-files/typescript/modules/external-module
.
Let's say I have a JavaScript Node module defined in CommonJS format in a file called common.js
:
// common.js
module.exports = function () {
this.name = 'CommonJS Module';
};
In order to import this we need to do two things: first, we need to install Node's Type Definitions. Then we need to require the module. To install Node's Type Definitions run the following the terminal in the root of your project:
tsd install node --save
Now you should see a folder called typings
containing the type definitions. Now that we have Node's type definitions, let's add a reference to it on top of main.ts
:
// main.ts
/// <reference path="./typings/node/node.d.ts" />
and then we are going to require the module and log it to the console:
// main.ts
/// <reference path="./typings/node/node.d.ts" />
const common = require('./common');
console.log(common()); // --> CommonJS Module
After running the build task ( command + shift + b ), and running the file (F+5) you should see the following output:
node --debug-brk=32221 --nolazy run.js
Debugger listening on port 32221
CommonJS Modules
Note the configuration files that we are using:
tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"outDir": "output",
"out": "run.js",
"watch": true
}
}
launch.json
{
"version": "0.1.0",
"configurations": [
{
"name": "TS All Debugger",
"type": "node",
"program": "./run.js",
"stopOnEntry": false,
"sourceMaps": true
}
]
}