Building heterogeneous TypeScript libraries

Nov 21, 2012 TypeScript

A technique for compiling one or more TypeScript source files to a single JavaScript library file that can be used in both the browser and by Node.js applications.

By way of example

Our library source consists of a single TypeScript module called Lib spread across multiple source files (lib1.ts and lib2.ts) that exports a public API:

lib1.ts

module Lib {
    export function f() {}
}

declare var exports: any;
if (typeof exports != 'undefined') {
    exports.f = Lib.f;
}

lib2.ts

module Lib {
    export var v = {foo: 42};
}

declare var exports: any;
if (typeof exports != 'undefined') {
    exports.v = Lib.v;
}

The source file are compiled to a single JavaScript library lib.js using the TypeScript compiler:

tsc --out lib.js lib1.ts lib2.ts

lib.js

var Lib;
(function (Lib) {
    function f() {
    }
    Lib.f = f;
})(Lib || (Lib = {}));
if(typeof exports != 'undefined') {
    exports.f = Lib.f;
}
var Lib;
(function (Lib) {
    Lib.v = {
        foo: 42
    };
})(Lib || (Lib = {}));
if(typeof exports != 'undefined') {
    exports.v = Lib.v;
}

lib.js file now can be included on an HTML page with:

<script type="text/javascript" src="lib.js"></script>

Or in a Node.js application with:

var Lib = require('./lib.js');

The APIs are accessed via the module name e.g. Lib.f(), Lib.v.

Explanatory notes

The key to being able to import the code into Node.js with require('./lib.js') is conditionally assigning public API objects to properties of the global exports object e.g.

declare var exports: any;
if (typeof exports != 'undefined') {
    exports.f = Lib.f;
}

Scoping function wrapper

A variation of the above technique is to wrap the combined compiled file with a scoping function. This will ensure non-exported top level objects do not pollute the browser global namespace:

(function() {
  :
})();

The browser API is hoisted to the global namespace by assignment to the browser window object. The previous example becomes:

declare var exports: any;
if (typeof exports != 'undefined') {
    exports.f = Lib.f;
}
else if (typeof window !== 'undefined') {
    window['f'] = Lib.f;
}

References

« Previous Next »