TypeScript Modules – Part 4 | John Papa

John Papa

Evangelist on the loose

TypeScript Modules – Part 4

...

Most every team enjoys easy to maintain and well-organized code that follows a consistent pattern (whatever that pattern may be). When developing with JavaScript it is just as important to follow a pattern to help the developer behind you, perhaps because it is so easy not to. JavaScript is a dynamic language that provide great power. But with great power comes great responsibility. Otherwise, you can easily drive right off the map.

Ravioli Code

module ravioliAn analogy I enjoy relates modules to ravioli. Code without modules might be strewn all over the place, tangled, hard to locate where the functionality you need lives. This is much like spaghetti code where you can’t tell where one set of code begins and ends. Modules help keep code with a specific role contained within the module. This makes it easy to identify which module performs what task or service. By separating the code into modules, or ravioli, it’s much easier to identify which ravioli the code you are looking for resides in.

One easy way to help maintain code re-use and organize your code is with modules. There are patterns such as the Revealing Module Pattern (RMP) in JavaScript that make this quite simple, but the good news is that in TypeScript modules become even easier with the module keyword (from the proposed ECMAScript 6 spec).

Dan Wahlin and I recently released a new course for Pluralsight that focuses on providing the fundamentals to write application-scale code using TypeScript.
tslogo

This post provides an overview on what’s in the 4th module of our course. This is part 4 of 4 in an article series where I describe the different areas Dan and I feel are fundamental to learning TypeScript. You can find the other posts here:

What’s a Module?

If you work on a team and want to make it easier for the developer behind you, modules can certainly help with that. There are other benefits to using modules too:

  • Scoping of variables (out of global scope)
  • Code re-use
  • AMD or CommonJS support
  • Encapsulation
  • Don’t Repeat Yourself (DRY)
  • Easier for testing

There are others, but I think you get the point here. Can you write great code without modules? Sure! But the benefits to using modules far outweigh the learning curve. I’ve found that when I start a project it really helps to think about the functionality in terms of which modules I may need. It is a similar process that I use for any language, and it flows well with TypeScript.

TypeScript Modules

TypeScript categorizes modules into internal and external modules. I like to further break out the internal modules into Implicit and Named. Each are still just modules, but how you define them and use them is important. (These terms are my own, simply to help me categorize modules. You won’t find them in the TypeScript spec.) For example, if you want to use AMD or CommonJS, you want to look at external modules. If you want to organize your code into files and define your own module names , then internal named modules are a good choice.

Implicit Internal Modules

Implicit internal modules are the easiest to work with as you simply just write source code without worrying about modules. For example the following code is the entire contents of a file that defines a and creates an instance of a class in TypeScript.

class TestClass {
    private a = 2;
    public b = 4;
};
var t = new TestClass();

There’s a module in there. Do you see it? The entire set of code is in an implicit module that joins the global module / global scope / global namespace (whichever term you prefer).When you run this code you will find these variables on the window object. The point here is that you get a module out of the box as all variables are going to default tot he implicit module, the global module if you will, unless you specify otherwise.

Is this a good practice? I prefer not to go this route because it means you are putting everything in the global scope. Global variables are bad in general. However, it is important to know how your code will be treated if you ignore modules: you end up back with spaghetti.

Named Internal Modules

When you use the module keyword you are using (named) internal modules. All variables defined within the module are scoped to the module and removed from the global scope. The module itself is in the global scope (though you can nest modules too). The code below shows that the Rectangle class and rect variable are both scoped to the Shapes module. They cannot be accessed outside of the module, which is why the final like of code would show no members of Shapes.

module Shapes {
    class Rectangle {
        constructor (
            public height: number, 
            public width: number) {
	}
    }
    // This works!
    var rect1 = new Rectangle(10, 4);
}

// This won't!!
var rect2 = Shapes._________

Exports

You could take that previous example and export what you want out of the module. In other words, you can make internal aspects of the module accessible outside of the module using the export keyword.

module Shapes {
    export class Rectangle {
        constructor (
            public height: number, 
            public width: number) {
	}
    }
}
// This works!
var rect = Shapes.Rectangle(10, 4);

You an also extend internal modules, share them across files, and reference them using the triple slash syntax. Dan and I go deeper on this topic in module 4 of our course.

///<reference path="shapes.ts"/>

External Modules

When dealing with large JavaScript based applications it can be unwieldy to manage the sheer number of scripts you may have. The dependency chain itself can be daunting as well as knowing when to load which scripts. This is where using the AMD convention can really help. TypeScript supports AMD using external modules. This is a topic unto itself and Dan and I cover it in our course along with some recommendations on how to use it. For simple / small apps there may be no need to use AMD. But for apps that need to scale, TypeScript and AMD together can really shine. I also recommend using the require.js library to help implement AMD. You can read more about require.js in my post here or in module titled “SPA Basics: Separating the Ravioli” of my SPA course.

When using AMD (or CommonJS) and external modules, you can export an entire module and then import it into another module. This defines a dependency chain that AMD and require.js can manage for you.

// bootstrapper.ts file

// imports the greeter.ts file as the greeter module
import gt = module('greeter'); 
export function run() {
    var el = document.getElementById('content');
    var greeter = new gt.Greeter(el);
    greeter.start(); 
}
// greeter.ts file

// exports the entire module
export class Greeter { 
    start() {
         this.timerToken = setInterval(() => 
             this.span.innerText = 
             new Date().toUTCString(), 500);
    }
}

Future

There are a lot of great features in TypeScript and it’s still early. There is a roadmap that the TypeScript team has published and thankfully one of the features on that list is generics. I’d love to see that addition and frankly I’d love to see the modules fleshed out even more. The tooling is just OK in my opinion, but adding in Web Essentials 2012 really improves the experience markedly for development today. They also list async/await, mixins, tooling features, and further alignment with ECMAScript 6. I expect all of these to improve as the team approaches its full release.

tags: course javascript pluralsight ravioli typescript
  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1278

  • Adrian

    Excellent work John ! Using TypeScript with require.js is very nice feature.I’ve watched your videos on pluralsight about SPA and Knockout and I’m very interesting in this new way of web developing. Now our team is trying to develop “Hybrid SPA” application (ASP.NET MVC 4 + all this JavaScript stuff), but we wonder how to bind View Model with our HTML tags on page load. The View Model files are written in TypeScript, if we would like for example to bind all View Models in bootraper (executed in main Layout) also written using TypeScript we have to import all our View Models – which isn’t satisfying sollutuion. So we think to put some binding JavaScript code on particular Views, but there we couldn’t use TypeScript syntax, So my queston Is there any nice workaround about this problem, how this could be solved ? Is there bootstraper needed ?

    • John

      Hi Adrian. Thanks for the positive feedback. If you want to bind everything at once, then the bootstrapper is a nice place to kick that off. But if you want to compose them as you go, then you could create a module that binds a view to a viewmodels as needed. It is entirely up to you on how you do it. Binding as you go is more scalable, IMO. For example, you click on a route and then you grab the viewmodel associated with that route and its view and bind them on demand.

      • Adrian

        So to implement this “binding as you go” should I put some JavaScript binding code on my particular view, in @section of my MVC View Page (rendered on script tag on Layout page) ?
        Or maybe the better solution is to create general module for all my views in pure js (without TypeScript support) which will try to bind all my view models with appropriate div’s id. In this module I will have to use require.js syntax, not TypeScript, because if I will try to use TypeScript I must declare import statements on top of file which cause that all my view Models will be load on that page – which I don’t want and of course isn’t necessary.

        • John

          I would create a module that handles the composition/binding. Maybe call composer.js. When you navigate to the view, first go to composer and have it find the viewmodel for the view, then bind then. Something like this pseudo-code:

          // inside composer.js
          var compose = function(viewName){
              var viewModel = findVmByViewName(viewName);
              var viewHtml = fetchHtmlForTheView(viewName);
              var $viewDom = $(viewHtml);
              var ko.applyBindings($viewDom, viewModel);
              // expose this method
          }
          
          var findVmByViewName = function(viewName){
              // Search array of view to vm mappings
              // Create this & pass it to composer
              return vm;
          }
          
          var register = function(map){
              // Pass array of mappings of view to vm
              // expose this method
          }
          
  • Adrian

    Nice pice of code, but I still wonder how to connect this with my TypeScript. When my AMD View Models (written in TypeScript) should be loaded by require.js ? I don’t wont to load view models (js files by require.js), that don’t will be used on that page. I still want point that we are using ASP.NET MVC routing system.

    Is this “findVmByViewName” method responsive for that or maybe in view I should first invoke register method (passing for example an map array from bootstraper, but this will get all my view models js files), and then invoke compose() method which will do all necesary binginds ? Which solution is correct ?

    • John

      Adrian – AMD and require.js can load your viewmodels as needed, so you dont have to preload them. When you require('myViewModel') it will go get that viewmodel if it doesn’t already have it. The method I wrote in the comments is just pseudo-code, but ts the basic steps. Replace it with what you need and use require.js if that helps. Whether you use TypeScript or JavaScript shouldn’t matter. I wouldn’t put any code in the view.

      User clicks route –> some module handles route –> invokes composer module –> composer “requires” view and viewmodel –> composer binds them –> show the view

      I hope this helps. Good luck!

      • Adrian

        Thank you vey much for your help, this solve my problem :)

  • http://ayvazyan.net/ Michael

    Hi John, Thank you for your articles and courses! Do you have any plans on updating Code Camper to use Type Script? Does it make sense?

    • John

      Hi Michael,

      I have created a new version of Code Camper (leaner) that I will unveil soon. After that, if people are interested, I could create a short course on converting it to TypeScript. I’ve done some of it already, so it’s certainly feasible. Nothing is certain yet though. I only plan 1 course at a time :)

      • http://ayvazyan.net/ Michael

        Thank you for the quick response! Can’t wait to see a new version :)

  • Peter Backa

    Hello, is it possible to import amd module within another module or class? I mean with requirejs is it possible to conditionally load modules in runtime. From typescript i know just the method with import xy = module(“xy”), but this statement should be in global namespace? Or am i missing something?

    • John

      Peter – You can load a module from another module. It’s quite common to have module A depend on module B and B depends on module C.

      • Peter Backa

        Yes, but i would like to import module within class functions. Typescript will compile, only if i place the import module on the top of the typescript file in global namespace. There are cases, where you will import module based on condition and not globally.

  • Gouri Sankar

    Hi John, I am trying to convert the main.js in code camper using TypeScript. Its a nightmare for me to convert this file using typescript. Do you have an example for the same? This will be a life saver for me :)

  • Gouri Sankar

    Hi John when is your plan to convert code camper to Typescript? Eagerly waiting for this!!

    • John

      Gouri – It’s on my list. I’ve already done a rough pass at it and it works. But there are more features coming to TypeScript that I want to take advantage of. Things like Generics are on their roadmap. So until they are done, I will hold off.

    • Adrian

      Hi Gouri – if you are interested in conjunction main.js (bootstraper) with Classes wrote in Typescript, I’ve implemented some solution (with John’s help, see previous posts )
      which maybe useful for you and maybe other guys.

      I’m writting my app in Asp.net MVC with extensive use of require.js (AMD modules), Knockout and TypeScript. I don’t needed to convert main.js into TypeScript, because its code is very short. For me, the most heavy work do composer.

      Here is my structure:

      #1 main.js – wrote in JS, it only configures require.js lib
      #2 composer.js – wrote in JS, it contains code to asynchronous load my ViewModel Classes with require.js help, the core of this file is composer function (compare it with prevoius pseudocode):

      // load necessery js

      require(['app/' + viewModel], function (model) {

      var fn = eval(‘model.’ + viewModel);
      var vm = new fn(initialData);
      ko.applyBindings(vm, $viewDom.get(0));
      });

      #3 All my ViewModel Classes are wrote in TypeScript :)

      I hope that this will be usefull for somebody :) For me this solution works perfectly, and use of Knockout, require.js with TypeScript is awesome !

  • Gouri Sankar

    Thanks John, will wait for the course. Meanwhile I sorted out typescript conversion of Main.js. One thing i observed, If I put the external libraries inside require.config({ under path section.If debug is false, all the Js files bundles , but again request goes to load the js file under the path section. So basically for example infuser.js loads twice. I guess there will be effective way to handle this.

  • Gouri Sankar

    HI Adrian, I have sorted out the issue with main.js for typescript with help of John’s typescript tutorial. It will be great if you can share your code sample. I can proceed further in creating view models and models.

  • Anusha

    Thanks for this wonderful article, John. In modern apps, Javascript code can reference C# libraries. How would something like that be achieved using Typescript i.e. can typescript code reference C# library?

%d bloggers like this: