Importing AMD modules

Importing AMD modules

In the preceding recipe we learned that we can use the function named define to declare AMD modules in this recipe we are going to learn how we can use a function named require to import AMD modules.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 2.0 or higher.

How to do it

We can import a AMD module using a function named require:

require(['ndrscr', 'jquery'], function(_, $) {

  // Using Array module
  const result1 = _.arrays.first([5, 4, 3, 2, 1], 3);
  console.log(result1);

  const result2 = _.arrays.last([5, 4, 3, 2, 1], 3);
  console.log(result2);

  ///…
  
});

How it works

This function is defined by the AMD module loader and takes two arguments:

  • An array which contains the list of files that we wish to import.
  • A callback function which is invoked when all the dependencies have been loaded. As we can see in the preceding code snippet, we can change the name of the modules when they are passed to the callback function. For example, we changed ndrscr to _ and jquery to $. Remember that the define function is also able to import dependencies. Some developers wonder whether they should use require or define. In a real world application, the root module uses the require function and the rest of the modules use define function.

There’s more

It is also possible to conditionally import modules but you should try to avoid this as much as you can because it creates some problems with AMD module bundlers and is considered a bad practice:

require(['require', 'ndrscr'], function(require, _) {

  if(typeof $ === 'undefined') {
    const $ = require('jquery');
  }

  // ...
  
});

The best way to handle optional dependencies is via the AMD module loader configuration. We will learn more about it later in this chapter in the recipe about RequireJS.

See also

Please refer to the recipes about the declaration of AMD modules and RequireJS in this chapter to learn more about AMD modules.

Declaring AMD modules

Declaring AMD modules

In the preceding recipes we have learned about the TypeScript internal and external module definition syntaxes that we can use at design-time, we will now learn about the ones that we can use at run-time. Note that there are ways to use the AMD, CommonJS, UMD or SystemJS syntaxes at design time as well but it is considered a bad practice. If we use ES6 modules and design time, we can configure the TypeScript compiler to generate AMD, CommonJS, UMD or SystemJS modules. This is a great feature because we can abstract our code from the module system that will be used at runtime. However, if we use a module syntax AMD at design time and then we need CommonJS modules instead we will have to manually migrate from AMD to CommonJS.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 2.0 or higher, Gulp and the gulp-typescript plugin.

How to do it

If we have an ES6 module like the following one:

import { IArrays } from '../../interfaces';

class Arrays implements IArrays {
  // …
}

export default new Arrays();

We can compile it into an AMD module using the module compilation flag as we can see in the following Gulp task:

gulp.task('build-es6-to-amd', function () {
    const tsProject = ts.createProject('tsconfig.json', {
        typescript: require('typescript'),
        module: 'amd'
    });

    const input = './src/module_definitions/design_time/external/es6/*.ts';
    const output = './src/module_definitions/run_time/external/amd/';

    return gulp.src(input)
        .pipe(tsProject())
        .pipe(gulp.dest(output));
});

Note that the paths in the preceding Gulp tasks are the ones used in the companion source code. The ES6 modules are located in the following module:

/src/module_definitions/design_time/external/es6/

The resulting AMD modules are located under the following folder:

/src/module_definitions/run_time/external/amd/

How it works

After compiling our ES6 module into JavaScript, the resulting AMD module will look as follows:

define(["require", "exports"], function (require, exports) {
    "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
    var Arrays = (function () {
        // …
    })();
    exports.default = new Arrays();
});

As we can see a function named define in invoked. As we can guess by its name, the define function is used to declare a module. This function is part of the AMD module loader and takes two arguments arguments:

  • The first argument is an array of the dependencies of the module being declared.
  • AMD stands for Asynchronous Module Definition and that’s why the second argument is a callback. The AMD dependencies are loaded asynchronously and the callback in invoked when all the dependencies have been resolved.

In this case there are two dependencies:

  • The require function, which is part of the AMD module loader as well and can be used to require additional modules.
  • The exports object, which is used to declare the public parts of the module being defined. These two dependencies are always requested by the AMD modules generated by the TypeScript compiler but we can examine the ndrscr.js file to see how an AMD module with multiple dependencies and exports looks like:
    define(
      [
          "require", "exports", 
          "./arrays", "./collections", 
          "./functions", "./objects", 
          "./utility"
      ],
      function (
          require, exports, 
          _arrays, _collections, 
          _functions, _objects, 
          _utility
    ) {
       "use strict";
    
          var VERSION = "1.0.0", 
              arrays = _arrays.default, 
              collections = _collections.default, 
              functions = _functions.default, 
              objects = _objects.default, 
              utility = _utility.default;
            
          exports.VERSION = VERSION;
          exports.arrays = arrays;
          exports.collections = collections;
          exports.functions = functions;
          exports.objects = objects;
          exports.utility = utility;
    });
    
AMD modules can not be used at runtime without first loading an AMD module loader like RequireJS. The module loader will define the functions define and require, which are necessary to process with AMD modules at runtime. Also note that we don’t declare the file extensions because they are automatically appended by RequireJS. We will learn how to work with RequireJS towards the end of this chapter.

You may have noticed that the TypeScript compiler added the following line at the end of the module:

Object.defineProperty(exports, "__esModule", { value: true });

This line is necessary when working with some module loaders, in order to support the loading of AMD and CommonJS.

There’s more

AMD also allow us to import dependencies that will be used as global variables. These kind of dependencies are only required once in the entire application life cycle and they are not passed to the callback that is invoked when all the dependencies have been loaded. Usually, global dependencies are required in the root module but not necessarily used on it, which turns out to create a problem. Consider the following example:

import $ from 'jquery';
import Other from './other';

let x = new Other();

When compiled into JavaScript the compiler will not add a reference to jquery because it was not used in the file:

define(["require", "exports", "./other"], function(require, exports, Other) {
    var x = new Other();
});

The solution is to use an AMD dependency reference:

import 'jquery';
import Other from './other';

let x = new Other();
The preceding code snippet generates the following code:
define(["require", "exports", "./other", "jquery"], function(require, exports, Other) {
    var x = new Other();
});

Source Code

Declaring AMD modules

See also

Please refer to the ES6 modules recipes in this chapter to learn more about the declaration of external modules. Refer to the recipes about RequireJS in this chapter to learn how to configure and use RequireJS. You can also refer to chapter chapter 1: Accelerating your development with the best TypeScript tools to learn about Gulp.

Importing ES6 modules

Importing ES6 modules

Just like we did with the external legacy modules we can also import ES6 modules. Let’s learn how to we can do it.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 2.0 or higher.

How to do it

The import statement for ES6 looks as follows:

import arrays from './arrays';

How it works

As we can see in the preceding code snippet, this statement is really similar to the one that we used in the recipes about legacy external modules earlier in this chapter. The main difference is that, instead of invoking the require function and assigning its return value to a variable named arrays, in this case, we are using the reserved keyword “from”.

Note that the preceding import statement will import only the element flagged as default in the imported module.

There’s more

It is also possible to import multiple elements from a module that has multiple exports using curly brackets:

import { arrays1, arrays2 } from './arrays'; 

We can also import the default export of a module together with some non-default ones:

import arrays, { arrays1, arrays2 } from './arrays';

The preceding line of code imports the default export of the arrays module and stores into a variable named arrays. It then non-default imports the array1 and array2 imports. We can also create aliases for a module when importing it by using an asterisk:

import * as arraysAlias from './arrays'; 

The preceding line of code imports both, the default export and the non-default exports of the arrays module and stores them as properties of the arraysAlias alias. Aliases can also be applied to one element rather than the entire module:

import { arrays1 as arr1, arrays2 as arr2 } from './arrays'; 

See also

Please refer to the recipes about the declaration of ES6 modules and the recipes about external legacy modules to gain a better understanding about the TypeScript external modules landscape.

Please note that the default export statements like the following:

export default arrays;

Should be thought of as exporting a member on the module named default. ES6 modules understand how to handle default exports, which means that the following ES6 statement:

import arrays from './arrays';

Will import the arrays object. However, legacy external modules don’t understand how to manage default exports automatically. This means that the preceding statement is not semantically equivalent to the following legacy external modules statement:

import arrays = require('./arrays')

Which will import an object with a property named default and arrays as its values:

{ default: arrays }

Declaring ES6 modules

Declaring ES6 modules

ES6 modules, also known as ECMAScript modules are the new syntax for the declaration of modules. The ES6 module declaration syntax is based in the ECMAScript standards. As today we can use ES6 modules at design time but someday they will become the native module definitions syntax for all JavaScript applications.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 1.5 or higher.

How to do it

The library included in the companion source code declares a library called ndrscr. This library is implemented using ES6 modules under the following directory:

/chapter_03/src/module_definitions/design_time/external/es6

Declaring ES6 modules is almost identical to declaring external legacy modules:

import { IArrays } from '../../interfaces';

class Arrays implements IArrays {

  public first<T>(array: Array<T>, n = 1): Array<T> {
    const result = [];
    for (let i = 0; i < n; i++) {
      const value = array[i];
      if (typeof value === 'undefined') {
        return result;
      }
      result.push(array[i]);
    }
    return result;
  }

  public last<T>(array: Array<T>, n = 1): Array<T> {
    return this.first(array.reverse(), n);
  }
}

export default new Arrays();

How it works

As you can see the ES6 exporting syntax is almost identical to the legacy external modules syntax and it is also used at design time. However, you should try to use the ES6 syntax rather than the legacy external modules syntax when possible because the ES6 syntax the only real proposed standard.

There’s more

You can declare multiple exports:

export arrays;
export arrays1;
export arrays2;

You can also use an in-line style:

export { arrays, arrays1, arrays2 };

It is also possible to flag one of the exported elements as the default export:

export default arrays;
export arrays1;
export arrays2;

Or using the in-line style:

export { default arrays, arrays1, arrays2 };

It is important to note that only one of them can be declared as the default export.

Source Code

Declaring ES6 modules

See also

Please refer to the preceding recipe to learn more about internal modules and legacy external modules.

Importing legacy external modules

Importing legacy external modules

In the preceding recipe, we learned how to declare legacy external modules. In this recipe, we will learn how to consume already declared legacy external modules.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 2.0 or higher.

How to do it

We can import a legacy external module using the following syntax:

import arrays = require('./arrays');

How it works

When we use an import statement without curly brackets like the preceding one:

import arrays = require('./arrays');

The module export is automatically assigned to the variable in the import statement. This means that the value of the arrays variable in the preceding line of code will be the value of the default export in the arrays module:

arrays.first([5, 4, 3, 2, 1], 3);

Source Code

See also

Please refer to the preceding recipe to learn about the declaration of external legacy modules.

Declaring legacy external modules

Declaring legacy external modules

We could arguably avoid mentioning legacy external modules in this book because we should try to avoid using them but it is possible that we will encounter some code samples on the internet or old type definitions files that are still using them. It is important that we cover them so we can migrate them into the new recommended module declaration syntax for external modules: ES6.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 2.0 or higher.

How to do it

The library included in the companion source code declares a library called ndrscr. This library is implemented using legacy external modules under the following directory:

/chapter_03/src/module_definitions/design_time/external/external

Let’s take a look to the arrays.ts file:

import { IArrays } from '../../interfaces';

class Arrays implements IArrays {
  public first<T>(array: Array<T>, n = 1): Array<T> {
    const result = [];
    for (let i = 0; i < n; i++) {
      const value = array[i];
      if (typeof value === 'undefined') {
        return result;
      }
      result.push(array[i]);
    }
    return result;
  }

  public last<T>(array: Array<T>, n = 1): Array<T> {
    return this.first(array.reverse(), n);
  }
}

export default new Arrays();

How it works

It is important to understand that external legacy modules are used at design time. The arrays module declares a class names Arrays. This class has two methods. The first one is named first and it can be used to get the first n elements of an array:

first([5, 4, 3, 2, 1], 3); // [5,4,3]

The second method is named last and it can be used to get the last n elements of an array:

last([5, 4, 3, 2, 1], 3); // [1,2,3]

Then an instance of the class Arrays is created and, finally, it is exported using the export keyword.

Source Code

Declaring legacy external modules

See also

Please refer to the next recipe (Importing legacy external modules) to learn how you can consume legacy external modules.

Running namespace & legacy internal modules

Running namespace & legacy internal modules

In this recipe we will learn about the different ways in which a library or application build with namespace (or legacy internal modules).

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 2.0 or higher.

How to do it

The library included in the companion source code declares a library called ndrscr.

Note that the ndrscr module is named ndrscr_with_namespaces in the namespaces sample included in the companion source code to avoid conflicts with the example that uses legacy internal modules (ndrscr_with_internal_modules).

This library is implemented using namespaces under the following directory: /chapter_03/src/module_definitions/design_time/internal/namespaces To load the JavaScript code generated for some namespaces you must:

  • Compile each of the files that declare the namespaces
  • Create a html file and
  • Load each of the resulting JavaScript file using a script tag:
<script src="arrays.js"></script>
<script src="collections.js"></script>
<script src="functions.js"></script>
<script src="objects.js"></script>
<script src="utility.js"></script>
<script src="ndrscr.js"></script>

How it works

Loading namespaces or legacy internal modules in a web browser is really simple. All we need to do is to use a script tag to load the contents of the files which declare the namespaces. Namespaces and legacy internal modules are good way to do a quick test of some concept or idea but they can lead to some problems.

  • The first problem is that it is hard to prevent this kind of modules from creating variables in the global scope (which is considered a bad practice by the TypeScript and JavaScript development communities).
  • The second problem is that we used many script tags to load the library and this will have a negative effect in our application’s performance.
  • The third problem is that we will not be able to load internal modules in a Node.js application because Node.js was designed to work with CommonJS modules and not global variables. We could solve these problems using some external tools but that would lead us to do some extra work that could be avoided by using external modules. For above reasons you should try to use external modules when possible instead of internal modules.

There’s more

It is possible to use the TypeScript compiler to generate one single file which contains all the modules in the application. The following gulp tasks showcases how to do it:

gulp.task('build-namespaces', function () {
    const tsProject = ts.createProject('tsconfig.json', {
        typescript: require('typescript'),
        'out': 'bundle.js'
    });
    const input = './src/module_definitions/design_time/internal/namespaces/*.ts';
    const output = './src/module_definitions/run_time/internal/namespaces/';

    return gulp.src(input)
        .pipe(tsProject())
        .pipe(gulp.dest(output));
});

The compiler options are set in the tsconfig.json file but we have set the most important compiler option for this example in the gulp configuration file. The out options is used to indicate the name of the file that will contain all the modules. As a result, we will be able to load all our namespaces with one single script tag:

<script src="bundle.js"></script>
Please note that this example is included in the companion source code.

Source Code

Running namespace & legacy internal modules

See also

Please refer to the preceding recipes in this chapter about namespaces to learn how to declare and consume this kind of modules.

Importing namespaces & legacy internal modules

Importing namespaces & legacy internal modules

In the preceding recipe we learned how to declare namespaces and legacy internal modules let’s learn how can we import them.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 1.5 or higher.

How to do it

Let’s declare the following namespaces in a file named BasicOperator.ts:

namespace MyMathLybrary.BasicOperators {
  export function sume(x, y) {
    return x + y;
  }
}

Now declare a namespace in a file named Geometry.ts

namespace MyMathLybrary.Geometry {
  export const PI = 3.1416;

  export function areaOfCircle(radious) {
    return PI * (radious * radious);
  }
}

We can then access the MyMathLybrary module from other files as follows:

///<reference path="./BasicOperators.ts"/>
///<reference path="./Geomery.ts"/>

MyMathLybrary.BasicOperators.sume(2,2);
MyMathLybrary.Geometry.areaOfCircle(2);

How it works

To access a namespace from a third file you need at a reference to the files which declare the namespaces:

///<reference path="./BasicOperators.ts"/>
///<reference path="./Geomery.ts"/>

We can then reference to the namespace and sub namespaces using the name of the namespaces:

MyMathLybrary.BasicOperators.sume(2,2);
MyMathLybrary.Geometry.areaOfCircle(2);

See also

Please refer to the preceding recipe to learn more about the declaration of namespaces and legacy internal modules.

Declaring namespaces & legacy internal modules

Declaring namespaces & legacy internal modules

Namespaces are a kind of internal module. Namespaces were known as internal modules, in the past it caused some confusion within the TypeScript community and the TypeScript team decided to change their name to make them easier to understand.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 2.0 or higher.

How to do it

You can declare a using the namespace keyword:

namespace MyLybrary {
   export function someFunction(x, y) {
	  //...
	}
}

Namespaces used the module keyword before it was renamed by the TypeScript team:

namespace MyLybrary {
   export function someFunction(x, y) {
	  //...
	}
}

How it works

Both, the legacy syntax (module keyword) and the new syntax (namespace keyword) can be used at design time and generate the same JavaScript code:

var MyLybrary;
(function (MyLybrary) {
    function someFunction(x, y) {
      // ...
    }
    MyLybrary.someFunction = someFunction;
})(MyLybrary || (MyLybrary = {}));
Since the module keyword will potentially become deprecated at some stage you should try to use the namespace keyword rather than the module keyword.

As we can observe in the preceding code snippet, the TypeScript compiler uses an immediately-invoked function expression (IIFE) to wrap the contents of the module or namespace and declares a variable named MyLybrary in the global scope. When the first module is loaded MyLybrary is undefined and it will be initialize using an empty object literal:

(MyLybrary = {})

When other modules are loaded, the MyLybrary object will already be defined and the contents of the new module contents will be appended to it as properties.

Note that only the elements flagged with the export keyword are accessible from the outside of a module.

There’s more

Namespaces can contain sub-namespaces. To declare a sub-namespace, you can use the dot character when declaring the module name:

namespace MyLybrary.SubNamespace {
    export function someFunction(x, y) {	
      // ...
    }
}

Alternatively, you can declare nested namespaces:

namespace MyLybrary {
    namespace SubNamespace  {
		export function someFunction(x, y) {
			// ...
		}
	}
}

The two preceding code snippet generates the same code snippet:

var MyLybrary;
(function (MyLybrary) {
    var SubNamespace;
    (function (SubNamespace) {
        function someFunction(x, y) {
          // ...
        }
        SubNamespace.someFunction = someFunction;
    })(SubNamespace = MyLybrary.SubNamespace || (MyLybrary.SubNamespace = {}));
})(MyLybrary || (MyLybrary = {}));

See also

You can refer to the recipe about importing namespaces to learn how to consume internal modules after declaring them.

Becoming an expert on modules - Introduction

In the previous chapters you learned how to automate your TypeScript development environment. We also learned how to put into practice type annotations, function, classes and other object oriented programming principles and elements. Modules were deliberately omitted because they deserve their own chapter.

  • Introduction
  • Declaring namespaces & legacy internal modules
  • Importing namespaces & legacy internal modules
  • Running namespace & legacy internal modules
  • Declaring legacy external modules
  • Importing legacy external modules
  • Declaring ES6 modules
  • Importing ES6 modules
  • Declaring AMD modules
  • Importing AMD modules
  • Running AMD modules with Require.js
  • Declaring CommonJS modules
  • Importing CommonJS modules
  • Running CommonJS modules with Browserify
  • Declaring UMD modules
  • Importing UMD modules
  • Running UMD modules
  • Declaring SystemJS modules
  • Importing SystemJS modules
  • Running SystemJS modules with SystemJS
  • Running ES6 modules with webpack

Introduction

The usage of modules in TypeScript and JavaScript is a topic that often leads to confusion because the module ecosystem is quite extensive: We can declare internal and external modules at design-time using two different module definition syntaxes:

  • Internal (legacy) and namespaces (new).
  • External modules (legacy) and ES6 modules (new). There are more module definition syntaxes at runtime:
  • System
  • AMD
  • CommonJS
  • UMD
  • ES6 There are many available third party module loaders and module bundlers or optimizers:
  • RequireJS
  • SystemJS
  • Browserify
  • Webpack The best way to get out of this confusing landscape is to learn how to use each of the module definition syntaxes and third party module loaders.

You should try to avoid the usage of the module definition syntaxes known as “internal modules” and “external modules” because, they are expected to become legacy in the future as Anders Hejlsberg (creator of TypeScript) expressed when the support of ES6 modules arrive to TypeScript:

As ES6 modules gain adoption, Typescript’s original export-equals and import-equals declarations are expected to become legacy.

As Anders explained we can expect ES6 modules (also known as ECMAScript modules) to become the most widely used module definition syntax as it is the only real proposed standard.

You should try to use external modules over internal modules and namespaces when possible. We will cover how internal modules and namespaces later in this chapter but we can say that internal modules and namespaces were the first implementation of modules in JavaScript and, since they arrived, newer and better approaches have become available.

You can use modules in a JavaScript engine like the one in a web browser or in Node.js but not all the module declaration syntaxes are natively supported by all the environment. For example, Node.js supports CommonJS modules without the need of external module loaders, module bundlers or transpilers.

It is possible to run any kind of modules in any of the available environments, for example it is possible to run AMD modules in Node.js using RequireJS but this not recommended in real world applications because we would be introducing an unnecessary module loader and increasing the level of complexity of the application without additional benefits.

In general, you should use the best module syntax for each environment and try to use a module bundler or optimizer in production environments to boost the performance of your application.

If you are working on a library that will be distributed and used by other developers, you should ensure that your library can be consumed by any of the available module loaders and bundlers.

The following table displays the list of available modules at design time:

Syntax Type Compiles to
Internal modules Internal IIFE
Namespaces Internal IIFE
External modules External AMD, CommonJS, UMD and SystemJS
ES6 modules External AMD, CommonJS, UMD and SystemJS

However, at runtime the following module definitions syntaxes are available:

Syntax Type Node.js Support Browser Support
Namespaces (IIFE) Internal Not Supported Native
AMD External RequireJS RequireJS
CommonJS External Native CommonJS loader
UMD External Native or AMD loader CommonJS or AMD loader
SystemJS External SystemJS loader SystemJS loader
ES6 External Transpiler (Native in the future) Transpiler (Native in the future)

In this chapter we are going to examine each of the available module definition syntaxes and learn how to use some of the most popular module loaders and bundles.

About the companion source code

The companion source code of this chapter includes a library called ndrscr which is inspired by underscore.js. This library is implemented many times using each of the different module definition syntaxes. Each of the examples is located in a different folders and the folder structure should help you to identify the kind of module definition syntax used for the example contained in a particular folder:

The ndrscr library is composed of six files, the root module in the library is located in a file named ndscr.ts. The main module has a dependency on other five modules names arrays, collections, functions, objects and utility.

These modules contain a couple of utility methods each. For example, the arrays module contains methods to select the first or last n items in an array. We will not go into details about the library because it is well documented using comments in the code, we will focus on the modules rather than the code of the library.

├── ndrscr.ts
├── arrays.ts
├── collections.ts
├── functions.ts
├── objects.ts
└── utility.ts