はじめに
TypeScript の人気が高まっている昨今ですが、 JavaScript で書かれたライブラリが必要な場面があると思います。しかし、 JavaScript には型情報がないため、そのままでは TypeScript のコードから使用することができません。
そこで、今回は、 TypeScript のコードから JavaScript のライブラリを使うために、型定義ファイルを作成する方法を実例を交えて紹介します。
対応方法
JavaScript のモジュールを TypeScript で使用するには、型定義ファイルを用意するか、モジュールを require で読み込む必要があります。
型定義ファイルを用意する方法としては、以下の方法があります。
- npm で @types からインストールする
- 自分で型定義ファイルを作る
npmで@typesからインストールする
Definitely Typed は型定義ファイルのリポジトリで、数多くの JavaScript ライブラリの型定義ファイルを用意しています。既に型定義ファイルが用意されている場合、 npm でインストールすることができます。
1 | $ npm install @types/jquery --save-dev |
自分で型定義ファイルを作る
残念ながら型定義ファイルが用意されていない場合や自作のライブラリの場合、自分で型定義ファイルを作成する必要があります。
型定義ファイルは
.d.ts
という拡張子のファイルです。保存する場所はどこでも構いませんが、
@types
ディレクトリに保存することが多いようです。
型定義ファイルを読み込むには、
tsconfig.json
に型定義ファイルが保存されているパスを指定する必要があります。
1 2 3 4 5 6 7 8 9 10 11 | { "compilerOptions": { // 省略 "typeRoots": [ "./@types", // 自作の型定義ファイルを配置するディレクトリのパス "./node_modules/@types" ], // 省略 }, // 省略 } |
今回は、こちらを参考に Vue.js のプロジェクトの TypeScript のコードで、 Vue.Draggable を使う場合を想定して、型定義ファイルを紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | declare module "vuedraggable" { import Vue, { ComponentOptions } from "vue"; export interface DraggedContext<T> { index: number; futureIndex: number; element: T; } export interface DropContext<T> { index: number; component: Vue; element: T; } export interface Rectangle { top: number; right: number; bottom: number; left: number; width: number; height: number; } export interface MoveEvent<T> { originalEvent: DragEvent; dragged: Element; draggedContext: DraggedContext<T>; draggedRect: Rectangle; related: Element; relatedContext: DropContext<T>; relatedRect: Rectangle; from: Element; to: Element; willInsertAfter: boolean; isTrusted: boolean; } const draggableComponent: ComponentOptions<Vue>; export default draggableComponent; } |
注目すべきは、
declare module "vuedraggable"
で vuedraggableモジュール をグローバルに宣言している点です。
次に、 TypeScript の Declaration Space(宣言空間)について説明します。
Declaration Space
TypeScript には、 Type Declaration Space(型宣言空間)と Variable Declaration Space(変数宣言空間)という2種類の Declaration Space(宣言空間)があります。
Type Declaration Space
Type Declaration Space(型宣言空間)には型アノテーションとして使用できるものが含まれます。
1 2 3 4 5 6 | // クラス class HogeClass {}; // インターフェース interface HogeInterface {}; // エイリアス type HogeType {}; |
これらは、以下のように型名として使用することができます。
1 2 3 | var hogeClass: HogeClass; var hogeInterface: HogeInterface; var hogeType: HogeType; |
しかし、 Variable Declaration Space(変数宣言空間)には宣言されていないので、変数として使うことはできません。
1 2 | interfase HogeInterface {}; var hogeInterface = HogeInterface; // ERROR: "cannot find name 'HogeInterface'" |
ただし、例外としてクラスは変数として使うことができます。
1 2 | class HogeClass {}; var hogeClass = HogeClass; // OK |
Variable Declaration Space
Variable Declaration Space(変数宣言空間)には変数として使用できるものが含まれます。
1 2 3 4 5 6 | // 数値 var hogeNum = 111; // 文字列 var hogeStr = "hoge"; // オブジェクト var hogeObj = {}; |
これらは Variable Declaration Space だけに宣言されているので、型アノテーションとして使うことはできません。
1 2 | var hogeNum = 111; var otherHogeNum: hogeNum; // ERROR: "cannot find name 'hogeNum'" |
Module
モジュールには Global Module と File Module の2種類があります。 Global Module は global namespace に入り、コードのどこからでもアクセスできるため、 global namespace を汚染します。できる限り使うべきではありませんが、使用する場合には注意が必要です。
File Module
File Module は External Modules(外部モジュール)とも呼ばれます。ルートレベルに import または export が存在する場合、そのファイル内にローカルスコープが作成されます。
1 | export var hoge = 111; |
これらは import しない限り外部から使用することはできません。
1 2 | import { hoge } from "./hoge"; var hoge2 = hoge; // OK |
ESモジュールの構文
変数または型のエクスポートは export キーワードで行います。
1 2 3 4 5 6 7 8 | export let hoge = 111; export interface HogeInterface { foo: string; bar: number; } // デフォルトのエクスポート // 補完が効かないためあまり使わないほうが良い export default class HogeClass {} |
インポートは import キーワードで行います。
1 2 3 4 5 6 | import { hoge, HogeInterfade } from "./hoge"; import HogeHoge, { hoge, HogeInterfade } from "./hoge"; // 名前を変えてインポート import { hoge as otherHoge } from "./hoge"; // 1つの名前に全てのモジュールをインポート import * as hoge1 from "./hoge"; |
モジュールのパス
モジュールには相対パスでインポートする Relative path modules(相対パスモジュール)とモジュール名だけを指定する Dynamic lookup(動的ルックアップ)の2種類があります。
Dynamic lookup の場合、検索は Node.jsスタイルのモジュール解決によって行われます。いかに例を示します。
例)import * as hoge from “hoge”と書いた場合
はじめに、
./node_modules/hoge
がチェックされます。見つからなかった場合、次に、ある
../node_modules/hoge
がチェックされます。このルールでモジュールが見つかるか、ファイルシステムのルートに到達するまで続きます。
この時、チェックされる順番は以下のとおりです。
- hoge.ts(hoge.d.tsもしくはhoge.js)
- hoge/index.ts(hoge.d.tsもしくはhoge.js)
- hoge/package.json が存在し、 package.json の typesキー で指定されたファイル
- hoge/package.json が存在し、 package.json の mainキー で指定されたファイル
Namespace
namespace は JavaScrip tで使用される一般的で便利な構文を提供します。
以下のように定義すると、
1 2 3 4 | namespace Hoge { export foo = 123; export bar = "bar"; } |
以下のような JavaScript のコードを生成します。
1 2 3 4 | (function (Hoge) { Hoge.foo = 123; Hoge.bar = "bar"; })(Hoge || (Hoge = {})); |
これは、以下のように使用できます。
1 2 | console.log(Hoge.foo); // 123 console.log(Hoge.bar); // "bar" |
さいごに
TypeScriptでJavaScriptのライブラリを使用する方法を紹介しました。いかがでしたでしょうか。 TypeScript の型システムについて参考になれば幸いです。