🛬

これさえやれば大丈夫! TypeScriptのImportが取っ付きにくい人向け

2020/09/20に公開

(2014/6/1追記) 本記事はTypeScriptに対する知識が浅いうちに生意気にも書いた記事ですが、TypeScriptのimportはクライアントサイド(ブラウザ)向けとサーバサイド(Node.jsなどのCommonJS)向けで解釈が異なり、このためコンパイラtscへのオプション指示が必要になります。以下の本文で「コンパイラに対するオプションを与えないと怒られる」と書いたのはクライアント向けとサーバ向けの情報を混ぜて勘違いした結果です。

以下は、クライアントサイド向けとしてimportを使う際に書いた古い記事です。コメント欄にも有益な情報を頂いておりますので、併せてご参照ください。


TypeScriptによってJavaScriptもずいぶん書きやすくなりました。OOPとしてグローバル変数や静的関数は極力減らしたいところですが、jQueryなどのJavaScript資産を併用すると、まだまだそうもいきません。
そこで名前空間に相当するTypeScriptのmoduleを活用してソースファイルをスマートに個別管理しましょう。

moduleを使う上での問題点

  • ハマる。最初は何をしていいのか全然分からない。
  • どうもexportとかimportがあるっぽい。
  • 検索で出たありがたい情報を元にやってみても やたらとコンパイラに怒られる。

どうもmodule()require()も仕様が微妙なようで今後どうなるか正直分かりません。コンパイラに対するオプションを与えないと怒られるなど使い勝手もイマイチな印象。

解決法

四の五の言わず、公式ドキュメント(PDF) "10.3 Import Declarations"を読めば一発で解決しました。実験を重ねたのでその結果を記しておきます。

  • module宣言を使う。
  • 外部ファイルで使いたいもの(クラス、関数、変数、インタフェースなど)にexportを宣言。
  • モジュール外や別のモジュール内でimportを使い呼び出す。
  • module()require()も使わない。
moduleA.ts
module modA {
    export class clsA {
        
        n: number;
        
        constructor(n: number)
        {
            this.n = n;
        }
        
        times(x: number): number
        {
            return this.n * x;
        }
    }
}

moduleB.ts
/// <reference path="moduleA.ts"/>

module modB {
    import clsA = modA.clsA;
    
    export function run(): void
    {
        var obj: clsA     = new clsA(5);
        var times: number = obj.times(2);
        alert(String(times));
    }
}

modB.run();

おそらくTypeScript経験者であれば上記ソースだけで何をしているか理解頂けると思います。importするとき実はimport clsA = modA.clsA;で十分なようで、複雑そうなrequire()は使っていません。

ちなみにこのimport、後述のjsソースでも分かる通り、ただのvarです。

注意点

module全体をexportしようとしてexport moduleとすると怒られます。require()はこの場面で意義が出てくるようですが、スコープを絞る意味でも各関数、クラスごとにexport宣言したほうが理解しやすいと感じます。他言語でいうpublicprivateを使い分ける感覚です。

TypeScript言語仕様にもprivateはありますが、あれはコンパイラに対する指示であって、js変換後は結局どこからでも呼んでこれるので、スコープを限定するmoduleexportの組み合わせのほうが良い気がしています。

おまけ

最後に、これをJavaScriptに変換したあとのソースも貼っておきます。

run.js
var modA;
(function (modA) {
    var clsA = (function () {
        function clsA(n) {
            this.n = n;
        }
        clsA.prototype.times = function (x) {
            return this.n * x;
        };
        return clsA;
    })();
    modA.clsA = clsA;
})(modA || (modA = {}));

/// <reference path="moduleA.ts"/>
var modB;
(function (modB) {
    var clsA = modA.clsA;

    function run() {
        var obj = new clsA(5);
        var times = obj.times(2);
        alert(String(times));
    }
    modB.run = run;
})(modB || (modB = {}));

modB.run();

すごいね。


TypeScriptでの型宣言についてもまとめています。

Discussion