UMD
UMDモジュールとは、(インポートによる)モジュールとして、または(モジュールローダーなしの環境で実行される場合の)グローバルとして使用できるモジュールです。例えば、Moment.jsのような多くの人気のあるライブラリがこの方法で記述されています。例えば、Node.jsやRequireJSを使う場合、次のように記述します。
ts
import moment = require("moment");console.log(moment.format());
一方、プレーンなブラウザ環境では、次のように記述します。
js
console.log(moment.format());
UMDライブラリの識別
UMDモジュールは、モジュールローダー環境の存在をチェックします。これは、次のような簡単なパターンです。
js
(function (root, factory) {if (typeof define === "function" && define.amd) {define(["libName"], factory);} else if (typeof module === "object" && module.exports) {module.exports = factory(require("libName"));} else {root.returnExports = factory(root.libName);}}(this, function (b) {
ライブラリのコード、特にファイルの先頭でtypeof define
、typeof window
、またはtypeof module
のテストが見られる場合、それはほとんど常にUMDライブラリです。
UMDライブラリのドキュメントには、require
を示す「Node.jsでの使用」の例と、スクリプトをロードするために<script>
タグを使用する「ブラウザでの使用」の例もよく示されています。
UMDライブラリの例
ほとんどの人気のあるライブラリは、現在UMDパッケージとして利用可能です。例として、jQuery、Moment.js、lodashなどがあります。
テンプレート
モジュールには、module.d.ts
、module-class.d.ts
、module-function.d.ts
の3つのテンプレートが利用可能です。
モジュールが関数のように呼び出し可能である場合は、module-function.d.ts
を使用してください。
js
var x = require("foo");// Note: calling 'x' as a functionvar y = x(42);
脚注「ES6がモジュール呼び出しシグネチャに与える影響」を必ずお読みください
モジュールがnew
を使って構築可能である場合は、module-class.d.ts
を使用してください。
js
var x = require("bar");// Note: using 'new' operator on the imported variablevar y = new x("hello");
これらのモジュールにも同じ脚注が適用されます。
モジュールが呼び出し可能でも構築可能でもない場合は、module.d.ts
ファイルを使用してください。
モジュールプラグイン または UMDプラグイン
モジュールプラグインは、別のモジュール(UMDまたはモジュール)の形状を変更します。たとえば、Moment.jsでは、moment-range
はmoment
オブジェクトに新しいrange
メソッドを追加します。
宣言ファイルを作成する場合、変更対象のモジュールがプレーンモジュールかUMDモジュールかに関わらず、同じコードを記述します。
テンプレート
module-plugin.d.ts
テンプレートを使用してください。
グローバルプラグイン
グローバルプラグインは、グローバル変数の形状を変更するグローバルコードです。グローバル変更モジュールと同様に、これらは実行時の競合の可能性を高めます。
たとえば、一部のライブラリはArray.prototype
やString.prototype
に新しい関数を追加します。
グローバルプラグインの識別
グローバルプラグインは、一般的にドキュメントから簡単に識別できます。
次のような例が表示されます。
js
var x = "hello, world";// Creates new methods on built-in typesconsole.log(x.startsWithHello());var y = [1, 2, 3];// Creates new methods on built-in typesconsole.log(y.reverseAndSort());
テンプレート
global-plugin.d.ts
テンプレートを使用してください。
グローバル変更モジュール
グローバル変更モジュールは、インポート時にグローバルスコープの既存の値を変更します。たとえば、インポート時にString.prototype
に新しいメンバーを追加するライブラリが存在する可能性があります。このパターンは実行時の競合の可能性があるため、やや危険ですが、それでも宣言ファイルを作成できます。
グローバル変更モジュールの識別
グローバル変更モジュールは、一般的にドキュメントから簡単に識別できます。一般的に、グローバルプラグインに似ていますが、効果を有効にするにはrequire
呼び出しが必要です。
次のようなドキュメントが表示される場合があります。
js
// 'require' call that doesn't use its return valuevar unused = require("magic-string-time");/* or */require("magic-string-time");var x = "hello, world";// Creates new methods on built-in typesconsole.log(x.startsWithHello());var y = [1, 2, 3];// Creates new methods on built-in typesconsole.log(y.reverseAndSort());
テンプレート
global-modifying-module.d.ts
テンプレートを使用してください。
依存関係の消費
ライブラリには、いくつかの種類の依存関係がある場合があります。このセクションでは、宣言ファイルにそれらをインポートする方法を示します。
グローバルライブラリの依存関係
ライブラリがグローバルライブラリに依存している場合は、/// <reference types="..." />
ディレクティブを使用します。
ts
/// <reference types="someLib" />function getThing(): someLib.thing;
モジュールの依存関係
ライブラリがモジュールに依存している場合は、import
ステートメントを使用します。
ts
import * as moment from "moment";function getThing(): moment;
UMDライブラリの依存関係
グローバルライブラリから
グローバルライブラリがUMDモジュールに依存している場合は、/// <reference types
ディレクティブを使用します。
ts
/// <reference types="moment" />function getThing(): moment;
モジュールまたはUMDライブラリから
モジュールまたはUMDライブラリがUMDライブラリに依存している場合は、import
ステートメントを使用します。
ts
import * as someLib from "someLib";
UMDライブラリへの依存関係を宣言するために/// <reference
ディレクティブを使用しないでください!
脚注
名前の競合の防止
グローバル宣言ファイルを作成するときに、グローバルスコープで多くの型を定義できることに注意してください。プロジェクトに多くの宣言ファイルがある場合、解決できない名前の競合を引き起こす可能性があるため、これは強く推奨されません。
従うべき簡単なルールは、ライブラリが定義するグローバル変数によって名前空間化された型のみを宣言することです。たとえば、ライブラリがグローバル値「cats」を定義する場合、次のように記述する必要があります。
ts
declare namespace cats {interface KittySettings {}}
しかし、そうではありません
ts
// at top-levelinterface CatsKittySettings {}
このガイダンスは、宣言ファイルのユーザーを壊すことなく、ライブラリをUMDに移行できるようにすることも保証します。
ES6がモジュールプラグインに与える影響
一部のプラグインは、既存のモジュールに対してトップレベルのエクスポートを追加または変更します。これは CommonJS や他のローダーでは合法ですが、ES6 モジュールは不変と見なされるため、このパターンは実現できません。TypeScript はローダーに依存しないため、コンパイル時のポリシーの適用はありませんが、ES6 モジュールローダーへの移行を予定している開発者は、この点を認識しておく必要があります。
ES6がモジュールの呼び出しシグネチャに与える影響
Express のような多くの人気ライブラリは、インポート時に呼び出し可能な関数として自身を公開します。たとえば、一般的な Express の使い方は次のようになります。
ts
import exp = require("express");var app = exp();
ES6 モジュールローダーでは、トップレベルのオブジェクト (ここでは exp
としてインポート) はプロパティのみを持つことができ、トップレベルのモジュールオブジェクトは決して呼び出し可能ではありません。ここでの最も一般的な解決策は、呼び出し可能/構築可能なオブジェクトに default
エクスポートを定義することです。一部のモジュールローダーシムは、この状況を自動的に検出し、トップレベルのオブジェクトを default
エクスポートに置き換えます。
ライブラリファイルのレイアウト
宣言ファイルのレイアウトは、ライブラリのレイアウトを反映する必要があります。
ライブラリは、次のように複数のモジュールで構成できます。
myLib +---- index.js +---- foo.js +---- bar +---- index.js +---- baz.js
これらは次のようにインポートできます。
js
var a = require("myLib");var b = require("myLib/foo");var c = require("myLib/bar");var d = require("myLib/bar/baz");
したがって、宣言ファイルは次のようになります。
@types/myLib +---- index.d.ts +---- foo.d.ts +---- bar +---- index.d.ts +---- baz.d.ts
ts
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]// Project: [~THE PROJECT NAME~]// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>/*~ This template shows how to write a global plugin. *//*~ Write a declaration for the original type and add new members.*~ For example, this adds a 'toBinaryString' method with overloads to*~ the built-in number type.*/interface Number {toBinaryString(opts?: MyLibrary.BinaryFormatOptions): string;toBinaryString(callback: MyLibrary.BinaryFormatCallback,opts?: MyLibrary.BinaryFormatOptions): string;}/*~ If you need to declare several types, place them inside a namespace*~ to avoid adding too many things to the global namespace.*/declare namespace MyLibrary {type BinaryFormatCallback = (n: number) => string;interface BinaryFormatOptions {prefix?: string;padding: number;}}