Общо показвания

октомври 05, 2012

TypeScript Part2: AMD and CommonJS

This is the second part of three part post dedicated to the developer's experience with the TypeScript language, created and released as open source by Microsoft.

Part 1 is here.

AMD modules.

The compiler supports producing AMD compatible modules out of your code. However one need to specifically target the currently circulating module systems when coding, which is fairly simple: just avoid the module keyword and mark the exports with the export keyword. This is slightly counter-intuitive, but if you know about this feature in advance no mistakes will be made. So to sum it up and clarify I will make a real world example and define the smjs api interface present on the stb devices:


interface Smjs {
  initapi();
  set_json_handler(hanlder: string);
  jsoncmd(command: string);
}

class esmjs implements Smjs {
  initapi() {}
  set_json_handler(handler) {}
  jsoncmd(srt){}
};

export var smjs = new esmjs();

Then I compile it with the following command:
tsc --module amd mymodule.ts
and the result is then:
define(["require", "exports"], function(require, exports) {
    var esmjs = (function () {
        function esmjs() { }
        esmjs.prototype.initapi = function () {
        };
        esmjs.prototype.set_json_handler = function (handler) {
        };
        esmjs.prototype.jsoncmd = function (srt) {
        };
        return esmjs;
    })();    
    ; ;
    exports.smjs = new esmjs();
})

This will allow me to basically define my module as amd without having to write my amd boilerplate. How about required modules? It is as simple as stateing them with an import module pattern:

import myname = module('path/to/module');

The resulting code will entirely compatible, as if you typed it in the amd pattern.
There is one thing to mind however: the compiler does not understand the requirejs config options and will always traverse the paths in simple mode (i.e. as if you have not set any config on requirejs regarding paths). In the opinion of some folks using rjs this makes it impossible to use TypeScript in a real requirejs projects. However this is not my opinion, because the compiler does not try to do everything for the module loader, instead it simply provides means to allow code completion and basic checks on your module, so instead of really providing the code for the third party modules that you do not want to include in the base path of your *.ts scripts, provide the definition files instead. More details and examples can be found by browsing the example projects constructed with typescript on their website.

Producing commonJS module is similar: even without specifying the
--module
directive the compiler will output commonJS style code when it encounters the
export
keyword. The same module above will look like this:
var esmjs = (function () {
    function esmjs() { }
    esmjs.prototype.initapi = function () {
    };
    esmjs.prototype.set_json_handler = function (handler) {
    };
    esmjs.prototype.jsoncmd = function (srt) {
    };
    return esmjs;
})();
; ;
exports.smjs = new esmjs();
(Dont know why the extra semicolons though)

One interesting aspect of all this is that is you reference moduleA from moduleB, the compiler looks for it in order to make all possible checks on your code, so if you do not really have the required module (i.e. A) you should at least provide the definitions for it. With the AMD/CommonJS module pattern it is even easier as you only will provide the exports from the module, which is often a very simple, stripped interface (modules provide better implementation hiding, right?).

Another catch is how you define the referenced modules in AMD/CommonJS module definitions in TypeScript: in TypeScript module you do it by using reference comments, however in AMD/CommonJS you do it with import. You can of course use imports in TypeScript modules as well, but in a slightly different meaning. You can learn more about this in the video posted on their web site.

One note: if you tend to require lots and lots of modules that are not written in TypeScript and you do not want to spend time writing definition files it is probably better for you to not bother with TypeScript. Module migration from AMD to commonJS and from CommonJS to AMD can be achieved with other tools anyway.

In my research I found it more compelling to use the modules for nodejs projects, because there are so much npm modules each and every one of them with different interface. What would be really great is if the npm modules provide definition only and if possible with commends. This could easily lead to a tool that is able to generate documentation from definition files, which will be a double win: a) you can use TypeScript or JavaScript to develop your npm module, then you deploy the JavaScript and the definition file. It can be used both by an IDE and by a documentation tool. On the consumer of the module side same can be applied: if the consuming developer uses TypeScript he gets to use the definition file for auto completion and for documentation, if JavaScript is the preferred technology then still the definition file can be of great help on understanding the API instead of reading lots of documentation, sometimes not that well written and sometimes completely missing.

As of AMD, the reality of most projects is as follow: you pull up like 10 different pieces of libraries and framework parts and almost none of them is AMD compatible, then you spend a few days tweaking the shims for those to be loaded as they require and then you start writing your application code "as modules" in hope that those can be reused in other parts of your application or (hail Mary) in another project, the later of course is happening like... never. At the end you have a well defined modular code of course, but you have do many dependencies in each of those modules that you figure out you will never be able to use just one or two modules from this project because they will pull up at least 10 times mode dependencies. Because it is application logic code. One thing you can easily do is plug new features in such partitioned code, which is not a small win, but it does not pay back that much when you think of it.

Enough on my opinions on the module patterns in use.

The fact is if you use AMD and/or CommonJS TypeScript can support your code easily and you do not need to rewrite your existing code to TypeScript to benefit it, you only need the definitions.

Part 3 will take a look at a how TypeScript compares to Closure Tools.

Няма коментари: