lib.d.ts
特別な宣言ファイル
lib.d.ts
はTypeScriptをインストールしたときに付属しています。このファイルには、JavaScriptランタイムとDOMに存在するさまざまな一般的なJavaScriptを構成する機能のアンビエント宣言が含まれています。- このファイルは、TypeScriptプロジェクトのコンパイルコンテキストに自動的に含まれます
- このファイルの目的は、JavaScriptやTypeScriptのプロジェクトで、すぐに型のサポートを得られるようにすることです
- TypeScriptをサポートしているIDE(Visual Studio Code等)であれば、JavaScriptのプロジェクトであっても、型の恩恵を得ることができます
コンパイルオプションに
--noLib
を指定してこのファイルをコンパイルコンテキストから除外することができます(tsconfig.json
にnoLib : true
を指定)。いつものように、実際に使用されている例を見てみましょう:
var foo = 123;
var bar = foo.toString();
このコードは問題なく型チェックされます。なぜなら、すべてのJavaScriptオブジェクトに対して
toString
関数がlib.d.ts
で定義されているからです。noLib
オプションで同じサンプルコードを使用すると、型チェックエラーが発生します:var foo = 123;
var bar = foo.toString(); // ERROR: Property 'toString' does not exist on type 'number'.
これで、
lib.d.ts
の重要性を理解いただけたと思います。次にその内容を見てみましょう。lib.d.ts
の内容は、主に変数宣言の集まりです。例えばwindow
、document
、math
や、同様のインターフェース宣言Window
、Document
、Math
です。このドキュメントと型アノテーションを読む一番簡単な方法は、動くとわかっているコードをエディターで打ち込んでみることです。例えばIDEで
Math.floor
と打ち込み、F12を押下すると、定義に移動します(VSCodeはこれをサポートしています)。サンプルの変数宣言を見てみましょう。
window
は次のように定義されます:declare var window: Window;
この単純な
declare var
の後に、変数名(ここではwindow
)とアノテーションで指定されているインターフェース(Window
インターフェース)が続きます。これらの変数は、一般的にグローバルなインターフェースを指し示しています。例として、ここにWindow
インターフェースの小さな(実際には非常に大規模な)サンプルを提示します:interface Window extends EventTarget, WindowTimers, WindowSessionStorage, WindowLocalStorage, WindowConsole, GlobalEventHandlers, IDBEnvironment, WindowBase64 {
animationStartTime: number;
applicationCache: ApplicationCache;
clientInformation: Navigator;
closed: boolean;
crypto: Crypto;
// so on and so forth...
}
これらのインターフェースには、たくさんの型情報があることがわかります。TypeScriptが存在しない場合、これを頭で覚えておかなければなりません。コンパイラが理解している知識を、インテリセンスや自動補完のようなもので容易にアクセスできるようにすることができます。
これらのグローバルにインターフェースを使用していることは良いことです。なぜなら、
lib.d.ts
を変更することなく、これらのグローバルのインターフェースにプロパティを追加することができるからです。次に、この概念について説明します。TypeScriptの
interface
はオープンエンドなので、lib.d.ts
で宣言されたインターフェースにメンバを追加するだけで、TypeScriptはその追加を認識します。これらのインターフェースをlib.d.ts
に関連付けるには、これらの変更を グローバルモジュール で行う必要があることに注意してください。このために、 global.d.ts
という特別なファイルを作成することをお勧めします。ここでは、
window
、Math
、Date
に要素を追加する例をいくつか示します:単に
Window
インターフェースにものを追加します:interface Window {
helloWorld(): void;
}
これにより、あなたはそれを安全な方法で使うことができます:
// Add it at runtime
window.helloWorld = () => console.log('hello world');
// Call it
window.helloWorld();
// Misuse it and you get an error:
window.helloWorld('gracius'); // Error: Supplied parameters do not match the signature of the call target
グローバル変数
Math
はlib.d.ts
で定義されています(再度、あなたの開発ツールを使って定義に移動してください):/** An intrinsic object that provides basic mathematics functionality and constants. */
declare var Math: Math;
すなわち、変数
Math
はMath
インターフェースのインスタンスです。Math
インターフェースは次のように定義されています:interface Math {
E: number;
LN10: number;
// others ...
}
つまり、グローバル変数の
Math
に物を追加したいのであれば、それをグローバルインターフェースのMath
に追加するだけです。seedrandom
プロジェクト を参考に、グローバルMath
オブジェクトにseedrandom
関数を追加してください。これは非常に簡単に宣言できます:interface Math {
seedrandom(seed?: string);
}
これで、それを使うことができます:
Math.seedrandom();
// または
Math.seedrandom("何らかの文字列");
lib.d.ts
のDate
変数の定義を見ると、次のようになっています:declare var Date: DateConstructor;
DateConstructor
というインターフェースは、Date
グローバル変数を使って使うことができるメンバが含まれている点で、Math
とWindow
で見たものに似ています。 例えばDate.now()
です。これらのメンバに加えて、Date
インスタンス(例えばnew Date()
)を作成するためのコンストラクタのシグネチャが含まれています。 DateConstructor
インターフェースの断片を以下に示します:interface DateConstructor {
new (): Date;
// ... その他の構成シグネチャ
now(): number;
// ... その他のメンバー関数
}
datejs
というプロジェクトを例にして考えてみましょう。 DateJSは、メンバをDate
グローバル変数とDate
インスタンスの両方に追加します。したがって、このライブラリのTypeScriptの定義は、以下のようになります(ところで、コミュニティはすでにそれをあなたのために書いています):/** DateJS Public Static Methods */
interface DateConstructor {
/** Gets a date that is set to the current date. The time is set to the start of the day (00:00 or 12:00 AM) */
today(): Date;
// 続く
}
/** DateJS Public Instance Methods */
interface Date {
/** Adds the specified number of milliseconds to this instance. */
addMilliseconds(milliseconds: number): Date;
// 続く
}
これにより、型安全な方法で次のようなことができます:
var today = Date.today();
var todayAfter1second = today.addMilliseconds(1000);
文字列の
lib.d.ts
を調べると、Date
(String
グローバル変数、StringConstructor
インターフェース、String
インターフェース)のようなものが見つかるでしょう。しかし、注意すべき点の1つは、以下のコードサンプルで示すように、String
インターフェースは、文字列リテラルに対しても影響がある、という点です:interface String {
endsWith(suffix: string): boolean;
}
String.prototype.endsWith = function(suffix: string): boolean {
var str: string = this;
return str && str.indexOf(suffix, str.length - suffix.length) !== -1;
}
console.log('foo bar'.endsWith('bas')); // false
console.log('foo bas'.endsWith('bas')); // true
同様の変数とインターフェースは、
Number
、Boolean
、RegExp
などの静的メンバとインスタンスメンバの両方を持つ他のものにも存在し、これらのインターフェースはこれらの型のリテラルのインスタンスにも影響します。メンテナンス上の理由から、
global.d.ts
を作成することを推奨しました。しかし、あなたが望むのであればファイルモジュールの中からグローバルな名前空間に入れることができます。これはdeclare global {/ * global namespace here * /}
を使って行います。例えば。前の例は次のようにすることもできます:// Ensure this is treated as a module.
export {};
declare global {
interface String {
endsWith(suffix: string): boolean;
}
}
String.prototype.endsWith = function(suffix: string): boolean {
var str: string = this;
return str && str.indexOf(suffix, str.length - suffix.length) !== -1;
}
console.log('foo bar'.endsWith('bas')); // false
console.log('foo bas'.endsWith('bas')); // true
前に述べたように、
--noLib
のコンパイラフラグを使用すると、TypeScriptは自動的にlib.d.ts
を除外し ます。これが有用である理由はさまざまです。一般的なもののいくつかを以下に示します。- 標準ブラウザベースのランタイム環境とは大きく異なるカスタムJavaScript環境で実行する場合
- コード内で使用可能なグローバルを厳密に制御したい場合。例えばlib.d.tsは
item
を大域変数として定義していますが、あなたはそれをコードに混入させたくないでしょう
デフォルトの
lib.d.ts
を除外すると、コンパイルコンテキストに同様の名前のファイルを含めることができ、TypeScriptは型チェックのためにそれを取り込みます。注意:--noLib
には注意してください。一度noLibを使ったら、あなたのプロジェクトを他の人と共有しようとしたとき、noLibを(またはあなたのLibを)使うことを強制することになります。さらに悪いことに、もし彼らのコードをプロジェクトに持っていこうとすると、あなたのlibに基づくコードに変換する必要があるかもしれません。
コンパイラのターゲットを
es6
に設定するとlib.d.ts
はPromise
のようなより現代的なもの(es6)のための環境宣言を追加します。コンパイラターゲットがコードの環境を変えるという魔法の効果は、一部の人にとっては望ましいことです。他の人にとっては、コードと環境を合わせないといけないので、問題があります。しかしそれでも、あなたの環境をきめ細かく制御したい場合は、次に述べる
--lib
オプションを使うべきです。場合によっては、コンパイルターゲット(生成されたJavaScriptのバージョン)と環境ライブラリサポートの関係を切り離したい場合があります。2016年6月において一般的な例は、
Promise
です。たいていは--target es5
のようにすることを望むと思います。しかし、それでも、Promise
のような最新の機能を使いたい場合、これをサポートするために、lib
コ ンパイラオプションを使ってlib
を明示的に制御することができます。注意:--lib