9. Modules

JavaScript has been around a long time - over 20 years, as of the writing of this book! And, only in recent years has the industry really started to take JavaScript seriously and started to apply patterns, practices, and development standards when working in the browser. A few sections ago, I pointed out the problem that is still chief among the issues that lingers today - putting all of your code in the global namespace.

Putting all your code in the global namespace encourages you to create all sorts of dependencies between components simply because you can. You need to share a value between to classes? Put it into a global variable! Why not - both components have access to it, right?

This kind of coding practice produces something that is affectionately nick-named “spaghetti code” because your entire application just becomes one giant ball of intertwined threads without any way of telling where one component ends and another begins.

To avoid spaghetti code, many first-class languages have some kind of mechanism to modularize code - to keep components separate from one another, and distinguish between two components that may share the same name but are otherwise completely different.

Given the absence of any JavaScript syntax prior to ECMAScript 2015 to support the modularization of code, the development community had to get creative and come up with a variety of design patterns to both encapsulate the internal workings of components and help organize growing codebases.

Among the many great resources that define these patterns is the book “Learning JavaScript Design Patterns” by Addy Osmani. The physical version of this book is available for purchase, however you can find the entire manuscript for free online - just check the course notes for the link.

In this book, Addy discusses several concepts that are particularly relevant when it comes to modularizing and organizing JavaScript codebases. The first is two different design patterns - the module pattern and the revealing module pattern. The next is the relatively simple and straight-forward concept of namespaces. And, the third is the slightly more complex concept of modules and module loaders.

In the rest of this chapter, I will explain all of these concepts to you, as well as show you how ECMAScript 2015 and TypeScript enable you to implement these best practices in your current codebases.

So, if you’re interested in learning about how to organize your code, check out the next chapter where I will introduce you to the simplest modularization concept: namespaces.

Organizing Your Code with Namespaces

In the previous section, I mentioned that TypeScript offers a few ways to better encapsulate and organize your code. In this section I’m going to show you the simplest of the techniques called “namespaces”.
Many modern programming language have a concept of namespaces - C, C++, Java, and C#, to name just a few - and the reason that namespaces are so popular is because they are an excellent way to avoid naming collisions and refer to a group of types as one organizational unit.

And, if you’ve used C, C++, and C# the TypeScript namespace syntax should feel incredibly familiar to you. It’s simply the keyword namespace, followed by the name of the namespace - any valid JavaScript variable name will do. For example:

namespace Model {
}

Or, if you like you can add some periods to add a hierarchy of namespaces all at once:

namespace TodoApp.Model {
}

Now, let’s take a look at the code that is generated when you do this:

‘Model.ts’

Well, actually, it’s nothing… Since there is no code in the namespace it has no functional effect so TypeScript simply ignores it when it comes to compiling the code to JavaScript.

So, I’ll add some code. Let’s start by moving the Todo interface in there:

namespace TodoApp.Model {
    interface Todo {
        id: number;
        name: string;
        state: TodoState
    }
}

If I look again, there’s still no code generated since interfaces also have no functional effect and get ignored.

‘Model.ts’

So, now I’ll get serious and add an enum which does require code to interact with at runtime:

namespace TodoApp.Model {

    interface Todo {
        id: number;
        name: string;
        state: TodoState;
    }

    enum TodoState {
        New = 1,
        Active,
        Complete,
        Deleted
    }
   
}

Which generates the following:

‘Model.js’
var TodoApp;
(function (TodoApp) {
    var Model;
    (function (Model) {
        var TodoState;
        (function (TodoState) {
            TodoState[TodoState["New"] = 1] = "New";
            TodoState[TodoState["Active"] = 2] = "Active";
            TodoState[TodoState["Complete"] = 3] = "Complete";
            TodoState[TodoState["Deleted"] = 4] = "Deleted";
        })(TodoState || (TodoState = {}));
    })(Model = TodoApp.Model || (TodoApp.Model = {}));
})(TodoApp || (TodoApp = {}));

Note that you are free to declare the same namespace multiple times, either in the same file or throughout multiple files, allowing you to organize your code however you like.

For example, I can split the TodoApp.Model namespace definition up into two parts:

namespace TodoApp.Model {

    interface Todo {
        id: number;
        name: string;
        state: TodoState;
    }

}

namespace TodoApp.Model {
    
    enum TodoState {
        New = 1,
        Active,
        Complete,
        Deleted
    }

}

Or, I can even define a whole new namespace in the same file:

namespace TodoApp.DataAccess {
    
    interface ITodoService {
        add(todo: TodoApp.Todo): Todo;
        delete(todoId: number): void;
        getAll(): Todo[];
        getById(todoId: number): Todo;
    }

}

Notice how - as soon as I put these types into their own namespace declarations - TypeScript begins to warn me that it doesn’t know about the Todo type in the ITodoService interface definition. In the next section I’ll get into the technical explanation as to why that happens even when two types share the same namespace, but the short answer is because in order to use types outside of the scope that you define them in, you need to expose them using the export keyword, like this:

export interface Todo {

export enum TodoState {

export interface ITodoService {

Now, that helps clean up reference between Todo and TodoState because they are in the same namespace, but I still have an issue when I try to refer to types across namespaces even when I export them, as is the case where the ITodoService in the TodoApp.DataAccess namespace is trying to refer to the Todo type in the TodoApp.Model namespace.

This is because I’m still referring to the global type Todo, which no longer exists.
It no longer exists because I took it out of the global scope when I moved it into its own namespace – which is the whole point of applying a namespace. Going forward, I now have to refer to it by it’s fully-qualified name, including the namespace.

In our example that means changing all references of Todo to TodoApp.Model.Todo:

export interface ITodoService {
  add(todo: TodoApp.Model.Todo): TodoApp.Model.Todo;
  delete(todoId: number): void;
  getAll(): TodoApp.Model.Todo[];
  sgetById(todoId: number): TodoApp.Model.Todo;
}

Alternatively, I can create an alias for the namespace or the type by using the import statement in my namespace declaration to import a type from another namespace:

namespace DataAccess {
    
    import Todo = TodoApp.Model.Todo;

    export interface ITodoService {
      add(todo: Todo): Todo;
      delete(todoId: number): void;
      getAll(): Todo[];
      getById(todoId: number): Todo;
    }

}

Either approach works just fine, and it’s up to you which approach you want to you for any given scenario. The general rule of thumb that I follow is to import a type or namespace if the namespace is very long or if you are going to refer to it more than a once or twice, as is the case in this example. Likewise, whether or not you choose to import the whole namespace or just one type at a time is completely up to you.

In this section, I showed you how to apply TypeScript’s namespace syntax to better organize your types and get them out of the global namespace. However, I started off the section by mentioning that organization is only one of the benefits of applying namespaces. In the next section I’ll show you how to take advantage of the other benefit that TypeScript namespaces provide and that is encapsulation and creating truly private variables and types.

Using namespaces to encapsulate private members

In the previous section I showed you how to leverage TypeScript’s namespace syntax to better organize your application code and get it out of the global scope. In this section, I’ll dive into how, exactly, namespaces work and how you can leverage them to define truly private variables and types.

I’ll start by checking out the code that gets generated when you define a TypeScript namespace.

// This...
namespace TodoApp.Model {
	export enum TodoState {
		New = 1
	}
}

// Gets generated as this:
var TodoApp;
(function (TodoApp) {
	var Model;
	(function (Model) {
		var TodoState;
		(function (TodoState) {
			TodoState[TodoState["New"] = 1] = "New";
		})(TodoState || (TodoState = {}));
		Model.TodoState = TodoState;
	})(Model = TodoApp.Model || (TodoApp.Model = {}));
})(TodoApp || (TodoApp = {}));

Take a good look, and keep this code in mind as I introduce you to a new JavaScript design pattern called an “Immediately-Invoked Function Expression”, (or “IIFE” for short). An IIFE is a popular pattern to encapsulate code while it executes and then choose which parts of the code will be exposed to the rest of the world by specifying them as the return value. Its syntax is pretty straight-forward:

First, create a normal function:

function defineType() {
}

Then, immediately follow the function with a set of parentheses, which immediately executes the function:

function defineType() {
}()

And, most implementations wrap the function in a set of parentheses, too, just to be safe:

(function defineType() {
})()

You can take the IIFE approach one step further by passing an object into the function that the function may refer to. The function then interact with that object either by referring to variables or functions on that object or building up the object by attaching additional members to it.

For example, I could rewrite my earlier jQuery plugin example like this:

var jQuery = {
    version: 1.19,
    fn: {}
};

(function createType($){
    
    if($.version < 1.15)
        throw 'Plugin requires jQuery version 1.15+'
    
    $.fn.myPlugin = function() {
        // plugin
    }
    
})(jQuery)

This example starts off by defining a variable named jQuery and giving it two properties: a version number and an fn object which I can attach custom functions to.

Notice how I did something a little tricky here and changed the name of the variable as it got passed in. Outside of the IIFE, the variable is named jQuery and that’s the object I pass in to the function when it gets immediately invoked on line 14. However, within my function I wanted to refer to the object using only the dollar sign, so I used the dollar sign as the name of the variable instead of calling it jQuery. The name really doesn’t matter, though – even though it’s called $ within the function and jQuery outside of the function, it still refers to the same exact object.

So, what do IIFEs have to do with TypeScript namespaces? Well, let’s go back to the code that TypeScript generated for the namespace declaration that I wrote:

var TodoApp;
(function (TodoApp) {
	var Model;
	(function (Model) {
		var TodoState;
		(function (TodoState) {
			TodoState[TodoState["New"] = 1] = "New";
		})(TodoState || (TodoState = {}));
		Model.TodoState = TodoState;
	})(Model = TodoApp.Model || (TodoApp.Model = {}));
})(TodoApp || (TodoApp = {}));

Even though it’s a whole lot more code, what you’re looking at here is actually just a bunch of IIFEs! That’s right - a TypeScript namespace is really nothing more than syntactic sugar to create an IIFE! That means that everything you write within the namespace declaration is automatically kept within the same scope and not allowed to leak out of that scope unless you explicitly expose it yourself.

This is why, in the previous section, I had to apply the export keyword to allow the Todo interface to reference the TodoState enum: even though they belonged to the same namespace, they were declared in two different namespace declarations and were private by default. The way the export keyword works is that it tells TypeScript to attach the type to the namespace object so that it can be accessed anywhere in the application - even in other declarations of the same namespace.

To really understand what’s happening, let’s walk through the generated code, line by line:

  1. The first thing to notice is that TypeScript generates the global object to hold the top-level namespace. In our example, this the TodoApp variable on line 1.
  2. The declaration of the global variable on line 1 is then followed by the beginning of the IIFE on line. I’ll get into the guts of the IFFE next, but for now I’ll jump all the way to line 13, where the IIFE ends and is immediately invoked. Look at the expression used to define the parameter being passed into the IIFE on this line: it basically says “if the global variable TodoApp already has a value, use that, otherwise, assign that global variable to an empty object and pass that in instead.” Regardless of whether the object existed before the declaration or not, by the time it gets passed as a parameter to the IIFE function on line 2, it has been defined.
  3. Since the TodoApp namespace is empty, there is no logic in it other than the declaration of the nested namespace Model , which follows the same exact approach to create the TodoApp.Model object as I just described for the TodoApp object.
  4. Finally, we get to the good stuff inside of the TodoApp.Model declaration. Here TypeScript generates the same code that we saw before to define the TodoState enum. And now that we know about the IIFE design pattern, we can see that enums and classes are declared using this pattern as well! The most interesting part of the Model namespace declaration is where the TodoState enum is exposed to the public by assigning it as a property of the Model namespace object. This line is the direct result of the export keyword, which I can prove by removing the export keyword and watching this line disappear: Model.TodoState = TodoState;. And, when I add the export keyword back, the line reappears.

When it comes to encapsulation, there are two very important points about what I just told you:

  1. Things declared within a namespace remain private unless you explicitly expose them, and
  2. When it comes down to it, namespaces are really just functions; special, powerful functions, sure… but just functions… and that means that you can define more than just types within them. You can also use them to create private variables and functions.

With that in mind, let’s see how I can take my TodoService and refactor it so that the private static variable and method that I’m currently using to generate unique IDs for my Todo items is truly inaccessible from other components in my application as opposed to simply telling TypeScript to yell at me whenever I try to access it.

I’ll start by moving the whole DataAccess namespace into its own file and copying the TodoService class into it:

DataAccess.ts
namespace DataAccess {
  
  import Model = TodoApp.Model;
  import Todo = Model.Todo;

  export interface ITodoService {
    add(todo: Todo): Todo;
    delete(todoId: number): void;
    getAll(): Todo[];
    getById(todoId: number): Todo;
  }
  
  class TodoService implements ITodoService {

    private static _lastId: number = 0;

    get nextId() {
      return TodoService._lastId += 1;
    }

    constructor(private todos: Todo[]) {
    }

    add(todo: Todo): Todo {
      todo.id = this.nextId;
      
      this.todos.push(todo);
      
      return todo;
    }
    
    delete(todoId: number): void {
      var toDelete = this.getById(todoId);
      
      var deletedIndex = this.todos.indexOf(toDelete);
      
      this.todos.splice(deletedIndex, 1);
    }

    getAll(): Todo[] {
      var clone = JSON.stringify(this.todos);
      return JSON.parse(clone);
    }
    
    getById(todoId: number): Todo {
      var filtered = 
        this.todos.filter(x => x.id == todoId);
        
      if( filtered.length ) {
        return filtered[0];
      }
      
      return null;
    }
  }

}

Then, I simply cut the lastId variable from inside the class and move it outside…

DataAccess.ts
  private static _lastId: number = 0;

  class TodoService implements ITodoService {

    private static _lastId: number = 0;

    get nextId() {
      return TodoService._lastId += 1;
    }

Remove the private and static keywords, which are no longer relevant, and replace them with a simple let statement:

DataAccess.ts
  private static _lastId: number = 0;
  let _lastId: number = 0;

  class TodoService implements ITodoService {

    get nextId() {
      return TodoService._lastId += 1;
    }

Finally, I’ll clean up any references to the static variable that are now broken.
Because the lastId variable is defined in the same scope that my class is defined in, my class can simply reference the variable by name:

DataAccess.ts
  let _lastId: number = 0;

  class TodoService implements ITodoService {

    get nextId() {
      return TodoService._lastId += 1;
      return _lastId += 1;
    }

I can even go one step further and replace the getter property method the same way: cut it, paste it outside of the class, and convert it into a normal function.
While I’m at it, I’ll rename it to generateTodoId():

DataAccess.ts
  let _lastId: number = 0;

  function generateTodoId() {
    return _lastId += 1;
  }

  class TodoService implements ITodoService {

    get nextId() {
      return _lastId += 1;
    }

Then I’ll update the reference to the getter method with a call to this private function instead…

DataAccess.ts
    add(todo: Todo): Todo {
      todo.id = this.nextId;
      todo.id = generateTodoId();

The end result of all of this is that I’ve got two private members that live outside of my class, completely encapsulated from the rest of the world, but fully accessible to any classes or functions that are defined within the same namespace declaration. Assuming that I really, really wanted to hide the implementation details of how I am generating new Todo IDs, this would be a great way to do it.

While incredibly effective, the namespace approach - also referred to as the internal module approach - is only one way that TypeScript enables you to encapsulate your components. The other way, referred to as the “external module” approach is equally effective and actually far more common in browser-based lazy-loading solutions such as require.js and in the NodeJS development world. To see what I’m talking about, check out the next section to see how implement the external module approach in TypeScript.

Understanding the difference between Internal and External Modules

In the previous section, I showed you how to leverage TypeScript namespaces, otherwise known as “internal modules”.
But, even though the internal module approach can be very effective, it’s actually fallen out of grace in recent years in favor of external modules, and in this section I’ll explain what external modules are and how you can start using them in your TypeScript applications.

Namespaces and the internal module approach allows you to group components together and get them out of the global scope by bringing them together under one umbrella object. Using TypeScript, you can control which components belong to which object by wrapping them with namespaces using the syntax that I demonstrated in the last section:

namespace TodoApp.Model {
  export interface Todo {
    id: number;
    name: string;
    state: TodoState
  }
}

And in that last section I also showed how you can use that same syntax to define multiple namespaces in the same file.

Put simply, the external module approach has most of the same goals of encapsulation and organization as the internal module approach, but its implementation is a bit different. Rather than using a keyword and brackets to indicate the scope of the module, the external module approach uses the file itself as the module scope.

Even under this approach, many of the same restrictions apply and perhaps the most important one of all is that all components defined within the module are only accessible to that file by default and must be exported in some way in order to be used.

The inverse is also true: any components defined outside of the module must be imported in order for the module to access them. This is in stark contrast to the traditional JavaScript development methodology of defining everything in the global scope, and even though I showed you in the last section how to use the import keyword to make an alias to another type or namespace, it’s really more of a shorthand than anything else – you don’t have to import another internal module in order to use it - you just… use it.

In the next few sections, I’m going to show you two different syntaxes that TypeScript provides for you to import modules: the “require” syntax (similar to the syntax that NodeJS uses) and ES2015 (the syntax that’s now part of the ECMAScript standard).

You may be scratching your head wondering why TypeScript offers two different syntaxes to import modules, and there’s a pretty simple answer for that: ECMAScript 6 (more commonly referred to as ECMAScript2015 when referring to the module syntax) defined its syntax after TypeScript had already implemented the require syntax. Since it couldn’t just drop support for a syntax that it’s had for a long time, now TypeScript supports both syntaxes.

As I’m showing you these two syntaxes, the thing to remember as you learn each of them is that they are completely functionally equivalent, by which I mean that TypeScript ends up generating the same exact code for them at compile time.

Ultimately, you can choose either syntax and be quite fine! However, as with most of the features that are in the ECMAScript specification, I would recommend using the ECMAScript syntax as this is the syntax that will eventually have native browser support.

Regardless of which syntax you choose, TypeScript supports two ways to import external modules so I’m going to show you how to use both of them. Either way, there is a little bit of configuration and preparation that you’ll need to do first, so head on to the next section where I’ll show you what you need to take care of in order to start using the external module approach.

Switching from internal to external modules

In the previous section I explained the concept of external modules and how they compare to the internal modules that I already demonstrated.

And, as I mentioned in that section, perhaps the biggest difference between the two module approaches is that external modules use the file that they’re defined in as their module border as opposed to an IIFE within a file. This important difference means that it is kind of redundant to apply internal namespaces within external modules, because the file already is the module.

For example, in the namespace section I introduced a namespace called DataAccess inside a file named DataAccess.ts. Once we switch to external modules in the next two sections, I’ll need to reference any types defined in this module by going through the external module first, then through the internal module, then finally get to the type I was looking for. In other words, I’ll end up writing this:

DataAccess.DataAccess.TodoService

And that’s just silly.

So, before I get into the next sections, I’m going to prepare by removing the internal modules that I added previously, but leave the export keyword on their public members because that syntax is the same syntax that you continue to use when exposing members in the external module approach. I’ll also remove those import statements that I was using as aliases because I’ll be replacing them in just a bit.

DataAccess.ts
namespace DataAccess {
  
  import Model = TodoApp.Model;
  import Todo = Model.Todo;

  // ... Rest of file
  
}

As I make these changes, I see that TypeScript is getting upset with me about the fact that I’m still using the export keyword even though I’m not defining any modules.
That is because in order to leverage the external module approach, I have to tell TypeScript that that’s what I’m doing.

I can tell TypeScript this and make these errors go away by adding another configuration parameter to my TypeScript configuration located in my tsconfig.json file:

The option I’m looking to add is the module compiler setting:

tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "module": "system",
    }
}

As I add this setting, Visual Studio Code offers several different autocomplete options for me to choose from: CommonJS, AMD, System, UMD, etc.
Keep in mind that this setting is really just telling TypeScript that I intend to use external modules by indicated what the compiled output should be.
As I mentioned in the previous section, you are free to use either syntax you like when actually defining the import, so it really doesn’t matter which option you choose here.
I’ve chosen the system setting because I’ll be implementing the System.js module loader in the next chapter, but you can feel free to choose whichever one you like.

Switching back to the DataAccess module I can see that the errors about the export keyword have gone away, but now it has no idea what the Todo type is.
That’s because I need to import that type from the Model namespace, so head on to the next section where I’ll show you how to use the require syntax to do exactly that.

Importing modules using Require syntax

In the previous few sections, I’ve been introducing the concept of external modules and explained that TypeScript supports two syntaxes for importing these modules: the require syntax and ECMAScript 2015 syntax and, in this section, I’ll be explaining the require syntax. The require syntax is pretty simple, so I’ll just go ahead and use it to show you what it looks like.

When I left off in the last section, TypeScript was giving me errors in the DataAccess.ts file because it didn’t know about the Todo type and I needed to import that type in order to make it happy again.

In order to do that, I simply write the following statement at the beginning of my file in order to reference the Model module…

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

This brings everything exported in the Model module into the scope of this file, all contained under the Model object, very similar to the internal module approach I showed earlier.

Notice that, while I did specify the path to the file I was looking for, I did not specify the extension of the file – in other words, I didn’t say Model.js or Model.ts. This is deliberate, and I’ll explain why in more detail in the next chapter when I discuss how modules are actually loaded, but for now this is an important part of the syntax to keep in mind.

And, like the internal module approach, I have two ways to reference components in this module: through the module object itself or by establishing a shortcut to them by giving them an alias.

For instance, if I want to refer to the Todo type, I can change every individual reference, like this:

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

export interface ITodoService {
  add(todo: Model.Todo): Model.Todo;
  delete(todoId: number): void;
  getAll(): Model.Todo[];
  getById(todoId: number): Model.Todo;
}

Or… I can establish one alias to the type, like this:

import Model = require('./Model');
import Todo = Model.Todo;

export interface ITodoService {
  add(todo: Todo): Todo;
  delete(todoId: number): void;
  getAll(): Todo[];
  getById(todoId: number): Todo;
}

You might think it’s a little annoying to have to do this in two lines – I sure do. In other words, I’d love to be able to write this:

import Todo = require('./Model').Todo;

Sadly, TypeScript doesn’t support this usage, so you’ll have to keep the two lines in place in order to create an alias like this.

import Model = require('./Model');
import Todo = Model.Todo;

However, the ECMAScript 2015 syntax does allow you to import individual types and create aliases for them all in one line. In fact, it’s one of the reasons that I prefer that syntax over this one.
To see what I’m talking about, check out the next section where I show you everything you need to know in order to use the ECMAScript 2015 module syntax.

Importing modules using ECMAScript 2015 syntax

A few sections ago, I mentioned that TypeScript supports two different syntaxes for importing external modules - the require syntax and the ECMAScript 2015 syntax - and in the last section, I showed you how to use the require syntax. In this section, I’ll show you how to use the ECMAScript syntax to import components from external modules. And, in fact, the ECMAScript 2015 syntax is actually pretty close to the “require” syntax.

As I showed in the previous section, you import a module with the “require” syntax like this:

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

But, with the ECMASCript 2015 syntax, you would write the same thing like this:

import * as Model from './Model';

That’s really all the difference! Once I’ve got the module referenced, the rest of the code works exactly the same way.
In other words, in order to refer to the Todo type in the Model module, I need to refer to it with its full name, like this:

let todo:  Model.Todo;

But, remember how, in the previous section, I used the import statement to give the Todo type an alias, like this?

import Todo = Model.Todo;
let todo: Todo;

Well, that same syntax will continue to work and I could use it here, but it’s not actually part of the ECMAScript 2015 spec… and that’s for a good reason: because the ECMAScript spec allows you to define aliases in the import statement itself!

To demonstrate, I’ll first remove the alias:

	import * as Model from './Model';
	import Todo = Model.Todo;

Then, I’ll set my focus on the original import statement, in particular the * as Model part.
What this part of the syntax is saying is, “import everything that is being exported from this module and attach it to a temporary object named Model so that I can refer to them in this file.

However, importing everything in the module like this is rarely want you want to do.
More often, you only need a few exports from a module, and in those cases you’ll want to import them individually, which you do by replacing this part of the statement with a set of brackets, like this:

import { } from './Model';

Then, inside of these brackets, you define every individual export that you want to import by name, separated by commas. In my example I just want to import the Todo type, which I can do like this:

import { Todo } from './Model';

As soon as I do that I can see it’s working because the TypeScript warnings about not knowing what the Todo type is go away.

Likewise, if I wanted to import more exports that this module provides, I could add them to the list, like this:

import { Todo, TodoState } from './Model';

Or, if you prefer to give the type an alias for when you’re working with it in this file, you can use that as syntax that I showed in the original import statement.

For example, if I didn’t like the name of the type Todo and I wanted to call it TodoTask instead, I could do this:

import { Todo as TodoTask, TodoState } from './Model';

Notice that as soon as I do this, TypeScript begins warning me again that it doesn’t know about a type named Todo – that’s because I just renamed it… well, in this file at least. In order to get rid of that error I have to use the new alias that I gave it, TodoTask:

let todo: TodoTask;

And, finally, it’s important to point out that you are not required to import anything at all, in which case you simply say “import module name”, like this:

import './Model';

Really the only scenario you’d want to do this is when you have a script that modifies the environment in some way that you are dependent on. In those cases, the import statement is still relevant because you do depend on that module getting loaded… you’re just not relying on any particular exports that the module provides.

And that’s how you use the ECMAScript module syntax! As I’ve mentioned several times now, even though this is one of the two syntaxes that TypeScript supports, I strongly recommend using the ECMAScript syntax if no other reason than it is a standard that will eventually have native browser support.

Now that I’ve demonstrated both syntaxes, head on to the next section where we’ll revisit that module configuration setting and see how it affects the code that TypeScript generates.

Loading External Modules

In the previous few sections I introduced you to the concept of external modules and two different syntaxes that are available to you to import one module from another. I also mentioned that - regardless of which of the two syntaxes you used to import modules - the code that gets generated is not only exactly the same, but also driven by a setting in the TypeScript configuration file tsconfig.json. In this section, I’ll not only talk about what each of those configuration options means, but I’ll also explain how you can actually use the external module approach in your application.

I’ll start by explaining what I meant by that last part about “actually using the external module approach”. You see, as of this writing, even though ECMAScript 2015 defines the syntax for importing one module into another, there are two problems:

  1. The syntax is not widely supported in browsers just yet, and
  2. There is no standard implementation for actually loading the modules when they’re referenced. As of this writing there is an ECMAScript loader specification that is in the process of gaining approval, but that means that native support for module loading is still very far off.

The net result of those two problems is that, until both of them are fixed, you’re probably going to want to introduce a new tool into your toolbelt and that is a module loader.

Choosing a module loader is easier said than done because there are plenty of options available and all of them have different ways of doing things. This is why TypeScript provides so many options for the module configuration setting: because there is one for every major module loader that’s available! So, the short answer to the question of which module configuration setting you should choose is: which module loader do you want to use?

Obviously, teaching you how to use every module loader out there is clearly outside the scope of this book (not to mention a whole lot of work). So, through the rest of the book I’ll be using the System.js module loader in order to load module files and manage module imports at runtime. The reason I’m choosing System.js over the others should come as no surprise – I’m choosing it because it is the loader that attempts to implement the proposed ECMAScript specification. In other words, my hope is that I can use it as a temporary solution until the browsers actually implement the ECMAScript proposal, at which time I should be able to simply drop the loader and use the native implementation. At least, I hope… in reality, I’ll probably end up needing to use System.js for longer than I expect, but that’s ok because at least it’s letting me write ECMAScript 2015 code now.

Now that I’ve chosen my module loader, I’ve got to update my index.html to change the way I’m loading my application.
Before using a module loader, I had to include every single JavaScript file explicitly, creating a <script> tag for each one. But, with a module loader, the only script you need to specify in your HTML is the module loader itself, then use the module loader’s API to call into your application module.

In other words, I can remove all of these script tags and replace them with a single reference to system.js.

index.html
  // All other script tags removed...
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/systemjs/\
0.19.22/system.js"></script>

Notice that I’ve taken a shortcut here and linked directly to the system.js file hosted on the Cloudflare CDN. This should work for you, but if it doesn’t you’ll need to download it and put it into your project locally, or use the npm command line tool to install the “systemjs” package:

npm install systemjs

Regardless of how you get System.js loaded, the next thing you’ll do is use it to load your module with this simple line (placed in a <script> block, of course):

<script type="text/javascript">
    System.import('app')
</script>

Note that at the time of this writing, I also had to add this configuration statement before the import call in order for it to work properly:

<script>
	System.defaultJSExtensions = true;
	System.import('app')
</script>

Hopefully by the time you’re reading this the TypeScript compiler or System.js will have been updated to remove the need for this line.

Once I have all this in place, I should be able to run the site and see it in action. Unfortunately, right now it doesn’t do anything, so let me write a little bit of code that loads the TodoService from the DataAccess module, initializes it with a Todo, and then prints out all the Todos onto the command line:

import { Todo, TodoState } from './Model';
import { TodoService } from './DataAccess';

let service = new TodoService([]);

service.add({ 
    id: 1, 
    name: 'Pick up drycleaning', 
    state: TodoState.New 
})

let todos = service.getAll();

todos.forEach(todo => 
    console.log(`${todo.name} [${TodoState[todo.state]}]`)
)

Now when I run the site and open up the console I should be able to see my Todo printed out indicating that everything has worked!

And with that, we’ve got a working external module implementation! Now, if you found this chapter perhaps a little overwhelming or even complicated, well… I don’t blame you. It actually took me a while to get comfortable with modules, too. It’s not really a concept that we JavaScript developers have really had to know until now! But, that’s ok for two reasons:

  1. If you take the time to learn how to use external modules properly, your code will become far more organized and easy to manage, and
  2. If you really don’t like the external module approach then you don’t have to use it! You can still use namespaces to implement an internal module approach that doesn’t require adding any more complexity or tools to your existing environment and yet still get the benefits of getting your code out of the global namespace!

It’s up to you what you decide to do, but I know for me, spending the time learning how to embrace external modules has really made my codebases far more enjoyable to work with, and I think it will be worth your time to learn, too.