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.


https://www.shivkushwaha.com

Shiv Kushwaha

Author/Programmer