モジュールの書き方

練習コードを書いてみて分かったことを備忘録的に書いてみる。
とりあえず、TypeScript っぽい構文のコードと JavaScript のコードを付きあわせて説明していくと思う。

% tsc --version
message TS6029: Version 1.6.2

空のモジュール

TypeScript っぽいコードだとこういう風にすごい簡単だけど:

module M {
}

M はとりあえずモジュールの名前だとする。
JavaScript だとこんなにめんどい:

var M = (function (M) {
    return M;
})( typeof M === 'undefined' ? M : {} );

匿名関数に渡している typeof M === 'undefined' ? M : {} はコンテキストとして使われる。

もうひとつのパターン

外側のモジュールを入れておく変数である M に代入しない方式もある:

var M;
(function (M) {
})( typeof M === 'undefined' ? M : {} );

この場合では:

var M;
(function (M) {
    function piyo() {
        console.log( 'Hello, World!' );
    }

    M.piyo = piyo;    // 必ず M.piyo に piyo を設定しておくこと。
})( typeof M === 'undefined' ? M : {} );

M.piyo();  // error! piyo が存在しない!!

みたいにモジュールを定義した後にモジュールの中身を使うことができない。
また、Mdefined な場合の {}exports とか module.exports にしておくと:

var M;
(function (M) {
})( typeof M === 'undefined' ? M : {} );

module.exports = M;

みたいに最後の方に module.exports = M というようなことをしなくても済む。

エクスポートしたいモジュール

TypeScript にはモジュールやクラス、関数に export と付けることで外部に見せたいなーという表明をすることができると私は理解しているんだけれど:

export module M {
    export function piyo() {
        console.log( 'Hello, World!' );
    }
}

というコードを tscコンパイルすると:

var M;
(function (M) {
    function piyo() {
        console.log('Hello, World!');
    }
    M.piyo = piyo;
})(M = exports.M || (exports.M = {}));

という感じになって:

var M = require( './M' );

M.piyo();    // error! piyo は存在しない!!!

はエラーになってしまう。
関数のエクスポート方法を見ると、別にこうでも良いのではないかという気がしている:

var M;
(function (M) {
    function piyo() {
        console.log('Hello, World!');
    }
    M.piyo = piyo;
})(M || {});
exports = M;

で、今になって理解したのだけど、M.M.piyo(); ならいけるのか。

var M = require( './M' );

M.M.piyo();    // => Hello, World!

そこら辺、よくわかっていないのでアレなんだけど、TypeScript は require 関連が何かおかしいという気がする。

こうなって欲しい

ということで、さっきの TypeScript っぽいコードが:

export module M {
    export function piyo() {
        console.log( 'Hello, World!' );
    }
}

コンパイルするとどーなって欲しいかというと、こうなって欲しい:

var M = (function (M) {
    function piyo() {
        console.log( 'Hello, World!' );
    }
    M.piyo = piyo;

    return M;
})( typeof M === 'undefined' ? M : module.exports );

モジュールで他のモジュールを使いたいなーという時はまた後ほど書きたいかもしれない。 おしまい。

参考