TypeScript tutorial: Lesson 16 – Module


To organize code, TypeScript use module.

Internal Module

Internal modules came in earlier version of Typescript. This was used to logically group classes, interfaces, functions into one unit and can be exported in another module. This logical grouping is named namespace in latest version of TypeScript. So internal modules are obsolete instead we can use namespace. Internal modules are still supported, but its recommended to use namespace over internal modules.

Internal Module Syntax (Old)

module MathModule { 
   export function add(x:number, y:number) {  
      console.log(x+y); 
   } 
}

Namespace Syntax (New)

namespace MathModule { 
   export function add(x:number, y:number) { console.log(x + y);} 
}

After compiling:

"use strict";
var MathModule;
(function (MathModule) {
    function add(x, y) {
        console.log(x + y);
    }
    MathModule.add = add;
})(MathModule || (MathModule = {}));

External Module

External modules in TypeScript exists to specify and load dependencies between multiple external js files. If there is only one js file used, then external modules are not relevant. Traditionally dependency management between JavaScript files was done using browser script tags (<script></script>). But that’s not extendable, as its very linear while loading modules. That means instead of loading files one after other there is no asynchronous option to load modules. When you are programming js for the server for example NodeJs you don’t even have script tags.

There are two scenarios for loading dependents js files from a single main JavaScript file.

  • Client Side – RequireJs
  • Server Side – NodeJs

Selecting a Module Loader
To support loading external JavaScript files, we need a module loader. This will be another js library. For browser the most common library used is RequieJS. This is an implementation of AMD (Asynchronous Module Definition) specification. Instead of loading files one after the other, AMD can load them all separately, even when they are dependent on each other.

Defining External Module
When defining external module in TypeScript targeting CommonJS or AMD, each file is considered as a module. So it’s optional to use internal module with in external module.

If you are migrating TypeScript from AMD to CommonJs module systems, then there is no additional work needed. The only thing you need to change is just the compiler flag Unlike in JavaScript there is an overhead in migrating from CommonJs to AMD or vice versa.

The syntax for declaring an external module is using keyword export and import.

Syntax

//FileName : SomeModule.ts 
export interface SomeInterface { 
   //interface body
}

To use the declared module in another file, an import keyword is used as given below. The file name is only specified no extension used.

import SomeModule = require("./SomeModule")

or

import {SomeInterface} = require("./SomeModule")

Example:

// IShape.ts 
export interface IShape { 
   draw:()=>void
}
 
// Rectangle.ts 
import shape = require("./IShape"); 
export class Rectangle implements shape.IShape { 
   public draw() { 
      console.log("Rectangle is drawn"); 
   } 
}
   
// Test.ts 
import shape = require("./IShape"); 
import rectangle = require("./Rectangle");  

function drawAShape(s: shape.IShape) {
   s.draw(); 
} 

drawAShape(new rectangle.Rectangle()); 

The command to compile the main TypeScript file for AMD systems is: tsc --module amd Test.ts

On compiling, it will generate following JavaScript code for AMD.

File: IShape.js

define(["require", "exports"], function (require, exports) {
    "use strict";
    exports.__esModule = true;
});

File Rectangle.js

define(["require", "exports"], function (require, exports) {
    "use strict";
    exports.__esModule = true;
    exports.Rectangle = void 0;
    var Rectangle = /** @class */ (function () {
        function Rectangle() {
        }
        Rectangle.prototype.draw = function () {
            console.log("Rectangle is drawn");
        };
        return Rectangle;
    }());
    exports.Rectangle = Rectangle;
});

File Test.js

define(["require", "exports", "./Rectangle"], function (require, exports, rectangle) {
    "use strict";
    exports.__esModule = true;
    function drawAShape(s) {
        s.draw();
    }
    drawAShape(new rectangle.Rectangle());
});

The command to compile the main TypeScript file for Commonjs systems is: tsc --module commonjs Test.ts

On compiling, it will generate following JavaScript code for Commonjs.

File: IShape.js

"use strict";
exports.__esModule = true;

File Rectangle.js

"use strict";
exports.__esModule = true;
exports.Rectangle = void 0;
var Rectangle = /** @class */ (function () {
    function Rectangle() {
    }
    Rectangle.prototype.draw = function () {
        console.log("Rectangle is drawn");
    };
    return Rectangle;
}());
exports.Rectangle = Rectangle;

File Test.js

"use strict";
exports.__esModule = true;
var rectangle = require("./Rectangle");
function drawAShape(s) {
    s.draw();
}
drawAShape(new rectangle.Rectangle());

Result:

D:\TypeScript\Module>node Test.js
Rectangle is drawn

Leave a Reply