相关文章推荐
逃跑的土豆  ·  pool.map() missing 1 ...·  2 年前    · 
任性的菠菜  ·  Power Automate 更新 ...·  2 年前    · 
メインコンテンツまでスキップ

型定義ファイル (.d.ts)

自身のプロジェクトでTypeScriptでコーディングする場合は型を宣言することにより、IDEやエディターの補完機能やコードチェックを行えます。しかし外部のパッケージ(npm)を利用する場合は型定義ファイルが含まれているとは限りません。

型定義ファイルとは

型定義ファイルとはアクセス可能な宣言を記述したファイルです。拡張子は .d.ts です。

型定義ファイルは主にパッケージを配布するために作成されます。TypeScriptはJavaScriptにコンパイルされるときに型情報は無くなってしまいます。そのままJavaScriptパッケージを利用すると型定義の恩恵を得ることができません。しかし型定義ファイルを同梱することにより補完やコードチェックとして利用することができます。

残念なことにnpmに公開されているすべてのパッケージに必ずしも定義ファイルが存在するとは限りません。こちらに関しては 型定義ファイルの有無 にて説明します。

型定義ファイル出力例

tscコマンドに -d オプションをつけてコンパイルを行うとJavaScriptと型定義ファイルを出力することができます。

TypeScriptファイル

次のTypeScriptファイル(sample.ts)を -d オプションを付けてコンパイルしてみます。

sample.ts
ts
interface Person {
firstName: string;
lastName: string;
}
 
function greeter(person: Person): string {
return "Hello, " + person.firstName + " " + person.lastName;
}
sample.ts
ts
interface Person {
firstName: string;
lastName: string;
}
 
function greeter(person: Person): string {
return "Hello, " + person.firstName + " " + person.lastName;
}

tscコマンドに -d オプションを付けコンパイルを実行する。

bash
tsc -d
bash
tsc -d

JavaScriptファイル

sample.tsではInterfaceを使っていますが、JavaScriptにはInterfaceの概念がないため関数のみになりました。また引数の型情報もなくなります。

sample.js
js
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
//# sourceMappingURL=sample.js.map
sample.js
js
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
//# sourceMappingURL=sample.js.map

d.ts ファイル

定義情報のみ記載されたファイルが出力されます。

sample.d.ts
ts
interface Person {
firstName: string;
lastName: string;
}
declare function greeter(person: Person): string;
sample.d.ts
ts
interface Person {
firstName: string;
lastName: string;
}
declare function greeter(person: Person): string;

型定義ファイルの有無

型定義ファイルはパッケージ開発者またはボランティアにより作成されています。

  • 型定義ファイル有り
    • TypeScriptで書かれたパッケージ
    • JavaScriptで書かれたパッケージだが .d.ts ファイルを同梱している
  • 型定義ファイル有りだが別途インストールが必要
    • JavaScriptで書かれたパッケージだが、 DefinitelyTypedに登録されている
  • 型定義ファイル無し
    • JavaScriptで書かれたパッケージで型定義ファイルが存在しない

型定義ファイル有り

NPMのパッケージの紹介ページを見るとパッケージ名称の右にTSのアイコンが表示されている場合があります。これは型定義ファイルが存在することを示しています。
これは、パッケージ開発者がTypeScriptで開発しているか、JavaScriptで開発しているが型定義ファイルを同梱していることを示しています。型定義ファイルが含まれているパッケージの場合は特別な作業は必要ありません。

例としてdate libraryの date-fns はJavaScriptで構築されていますが、 typings.d.ts を同封しています。そのままinstallを行うだけで定義ファイルの恩恵を受けられます。

bash
npm install date-fns
bash
npm install date-fns

型定義ファイル有りの場合は、設定なく型情報を参照することができます。

型定義ファイル有りだが別途インストールが必要

NPMのパッケージの紹介ページを見るとパッケージ名称の右にDTのアイコンが表示されている場合があります。これは型定義ファイルがこのパッケージ自身には含まれていないが、 DefinitelyTyped に登録されていることを示しています。
この場合は、パッケージをインストールした後に別途型定義ファイルをインストールする必要があります。定義ファイルのインストールも npm コマンドを利用します。

例として Express はJavaScriptで構築されていますが、型定義ファイルは @types/express というパッケージとして別途インストールする必要があります。

Express 本体と定義ファイルのインストール例は次のようになります。

bash
npm install express --save # express本体のインストール
npm install @types/express --save-dev # 型定義ファイルのインストール
bash
npm install express --save # express本体のインストール
npm install @types/express --save-dev # 型定義ファイルのインストール

型定義ファイル無し

型定義ファイルがないライブラリも存在します。その場合は

  1. any で妥協する
  2. 型定義ファイルを作る

型定義ファイルの存在しないライブラリも利用することが可能ですが暗黙的に any 型になります。また自身で作成しDefinitelyTypedに公開することもできます。

コントリビュート(貢献)する方法 | Definitely Typed

型定義ファイルで登場するキーワード

ここでは型定義ファイルを読めるようになるために、型定義ファイルでよく利用されるキーワードを紹介します。

declare

declare キーワードを使うことでTypeScriptに変数、関数、クラスなどがJavaScript内に「存在する」ことを伝えることができます。これを「アンビエント宣言」と呼びます。

次のファイルがJavaScriptライブラリとして読み込まれており、グローバル関数として hello が使える状態だとします。

js
function hello(name) {
return "Hello, " + name;
}
js
function hello(name) {
return "Hello, " + name;
}

この状態でTypeScriptで hello 関数を呼び出すと型エラーが発生します。これは、TypeScriptが hello 関数が存在することを知らないためです。

ts
hello("taro");
Cannot find name 'hello'.2304Cannot find name 'hello'.
ts
hello("taro");
Cannot find name 'hello'.2304Cannot find name 'hello'.

declare を利用してアンビエント宣言をすることで、TypeScriptにJavaScript内のどこかに hello 関数が「存在する」ことを宣言することができます。これによりTypeScriptが hello 関数を認識できるようになります。

ts
declare function hello(name: string): string;
 
hello("taro");
"hello, taro"
ts
declare function hello(name: string): string;
 
hello("taro");
"hello, taro"

実際のモジュールの型定義ファイルの例として jest の型定義ファイルを見てみましょう。 beforeAll などの関数が型定義ファイル内でアンビエント宣言されているのが確認できます。これによりモジュールの読み込みをしなくても、TypeScriptが beforeAll を関数として認識することができます。

node_modules/@types/jest/index.d.ts
ts
declare var beforeAll: jest.Lifecycle;
 
declare namespace jest {
type Lifecycle = (fn: ProvidesHookCallback, timeout?: number) => any;
}
node_modules/@types/jest/index.d.ts
ts
declare var beforeAll: jest.Lifecycle;
 
declare namespace jest {
type Lifecycle = (fn: ProvidesHookCallback, timeout?: number) => any;
}

namespace

namespace キーワードを使うことで名前空間を定義することができます。