TypeScriptでモジュールを使用する方法
著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
Modules は、コードをより小さく、より管理しやすい部分に編成する方法であり、プログラムがアプリケーションのさまざまな部分からコードをインポートできるようにします。 何年にもわたって、JavaScriptコードにモジュール性を実装するためのいくつかの戦略がありました。 TypeScript は、 ECMAScript 仕様とともに進化し、これらのさまざまな形式を柔軟に処理できるJavaScriptプログラムの標準モジュールシステムを提供します。 TypeScriptは、 ESモジュール構文と同様の統一構文でモジュールを作成および使用するためのサポートを提供し、開発者はNode.js(
このチュートリアルでは、TypeScriptでモジュールを作成して使用します。 独自のTypeScript環境でさまざまなコードサンプルに従い、import
およびexport
キーワードの使用方法、デフォルトのエクスポートを設定する方法、および上書きされたexports
を使用してファイルを作成する方法を示します。 ]コードと互換性のあるオブジェクト。
前提条件
このチュートリアルに従うには、次のものが必要です。
- TypeScriptプログラムを実行して、例に従うことができる環境。 これをローカルマシンに設定するには、次のものが必要です。
TypeScript関連のパッケージを処理する開発環境を実行するためにインストールされたNodeとnpm(またはyarn)の両方。 このチュートリアルは、Node.jsバージョン14.3.0およびnpmバージョン6.14.5でテストされました。 macOSまたはUbuntu18.04にインストールするには、「Node.jsをインストールしてmacOSにローカル開発環境を作成する方法」または「Ubuntu18.04にNode.jsをインストールする方法」の「PPAを使用したインストール」セクションの手順に従います。 これは、Windows Subsystem for Linux(WSL)を使用している場合にも機能します。 - JavaScript、特に destructuring、rest演算子、 imports /exportsなどのES6+構文に関する十分な知識が必要です。 これらのトピックに関する詳細情報が必要な場合は、JavaScriptシリーズのコーディング方法を読むことをお勧めします。
- このチュートリアルでは、TypeScriptをサポートし、インラインエラーを表示するテキストエディタの側面を参照します。 これはTypeScriptを使用するために必要ではありませんが、TypeScript機能をさらに活用します。 これらの利点を活用するには、 Visual Studio Code のようなテキストエディターを使用できます。このエディターは、TypeScriptをすぐにサポートします。
このチュートリアルに示されているすべての例は、TypeScriptバージョン4.2.2を使用して作成されています。
プロジェクトの設定
このステップでは、ベクトル演算を処理するための2つの小さなクラスVector2
とVector3
を含むサンプルプロジェクトを作成します。 この場合のvectorは、視覚的なグラフィックプログラムでよく使用される、大きさと距離の数学的測定値を指します。
作成するクラスには、ベクトルの加算という1つの操作があります。 後で、これらのサンプルクラスを使用して、あるプログラムから別のプログラムへのコードのインポートとエクスポートをテストします。
まず、サンプルコードを格納するディレクトリを作成します。
- mkdir vector_project
ディレクトリが作成されたら、それを作業ディレクトリにします。
- cd vector_project
プロジェクトのルートになったら、npm
を使用してNode.jsアプリを作成します。
- npm init
これにより、プロジェクトのpackage.json
ファイルが作成されます。
次に、TypeScriptを開発依存関係として追加します。
- npm install [email protected] --save-dev
これにより、TypeScriptコンパイラがデフォルト設定に設定された状態で、TypeScriptがプロジェクトにインストールされます。 独自のカスタム設定を行うには、特定の構成ファイルを作成する必要があります。
プロジェクトのルートにtsconfig.json
という名前のファイルを作成して開きます。 このチュートリアルの演習でプロジェクトを機能させるには、次の内容をファイルに追加します。
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"outDir": "./out",
"rootDir": "./src",
"strict": true
}
}
このコードでは、TypeScriptコンパイラに複数の構成を設定しています。 "target": "ES6"
は、コードがコンパイルされる環境を決定し、"outDir": "./out"
と"rootDir": "./src"
は、コンパイラの出力と入力をそれぞれ保持するディレクトリを指定します。 "strict": true
は、強い型チェックのレベルを設定します。 最後に、"module": "CommonJS"
は、モジュールシステムをCommonJS
として指定します。 これを使用して、Node.jsアプリケーションでの作業をシミュレートします。
プロジェクトを設定したら、基本的な構文を使用したモジュールの作成に進むことができます。
export
を使用したTypeScriptでのモジュールの作成
このセクションでは、TypeScriptモジュール構文を使用してTypeScriptでモジュールを作成します。
デフォルトでは、TypeScriptのファイルはグローバルスクリプトとして扱われます。 これは、ファイルで宣言された変数、クラス、関数、またはその他の構成がグローバルに使用可能であることを意味します。 ファイルでモジュールの使用を開始するとすぐに、このファイルはモジュールスコープになり、グローバルに実行されなくなります。
これを実際に表示するには、最初のクラスVector2
を作成します。 プロジェクトのルートにsrc/
という名前の新しいディレクトリを作成します。
- mkdir src
これは、tsconfig.json
ファイルでルートディレクトリ(rootDir
)として設定したディレクトリです。
このフォルダ内に、vector2.ts
という名前の新しいファイルを作成します。 このファイルをお気に入りのテキストエディタで開き、Vector2
クラスを記述します。
class Vector2 {
constructor(public x: number, public y: number) {}
add(otherVector2: Vector2) {
return new Vector2(this.x + otherVector2.x, this.y + otherVector2.y);
}
}
このコードでは、Vector2
という名前のクラスを宣言しています。このクラスは、プロパティx
およびy
として設定された2つの数値をパラメーターとして渡すことによって作成されます。 このクラスには、それぞれのx
値とy
値を組み合わせて、それ自体にベクトルを追加する1つのメソッドがあります。
ファイルは現在モジュールを使用していないため、Vector2
はグローバルスコープです。 ファイルをモジュールに変換するには、Vector2
クラスをエクスポートする必要があります。
export class Vector2 {
constructor(public x: number, public y: number) {}
add(otherVector2: Vector2) {
return new Vector2(this.x + otherVector2.x, this.y + otherVector2.y);
}
}
ファイルsrc/vector2.ts
は、Vector2
クラスという単一のエクスポートを持つモジュールになりました。 ファイルを保存して閉じます。
次に、Vector3
クラスを作成できます。 src/
ディレクトリ内にvector3.ts
ファイルを作成し、お気に入りのテキストエディタでファイルを開いて、次のコードを記述します。
export class Vector3 {
constructor(public x: number, public y: number, public z: number) {}
add(otherVector3: Vector3) {
return new Vector3(
this.x + otherVector3.x,
this.y + otherVector3.y,
this.z + otherVector3.z
);
}
}
このコードは、Vector2
に似たクラスを作成しますが、ベクトルを3次元で格納する追加のz
プロパティがあります。 export
キーワードによって、このファイルはすでに独自のモジュールになっていることに注意してください。 このファイルを保存して閉じます。
これで、vector2.ts
とvector3.ts
の2つのファイルができました。どちらもモジュールです。 各ファイルには、それらが表すベクトルのクラスである単一のエクスポートがあります。 次のセクションでは、import
ステートメントを使用してこれらのモジュールを他のファイルに取り込みます。
import
を使用したTypeScriptでのモジュールの使用
前のセクションでは、モジュールを作成する方法を見ました。 このセクションでは、これらのモジュールをインポートして、コードの他の場所で使用します。
TypeScriptでモジュールを操作する場合の一般的なシナリオは、複数のモジュールを収集し、それらを1つのモジュールとして再エクスポートする単一のファイルを作成することです。 これを示すために、src/
ディレクトリ内にvectors.ts
というファイルを作成し、このファイルをお気に入りのエディターで開いて、次のように記述します。
import { Vector2 } from "./vector2";
import { Vector3 } from "./vector3";
プロジェクトで使用可能な別のモジュールをインポートするには、import
ステートメントでファイルへの相対パスを使用します。 この場合、現在のファイルからファイルsrc/vector2.ts
およびsrc/vector3.ts
への相対パスである./vector2
および./vector3
から両方のモジュールをインポートしています。
ベクトルをインポートしたので、次の強調表示された構文を使用して、単一のモジュールでそれらを再エクスポートできます。
import { Vector2 } from "./vector2";
import { Vector3 } from "./vector3";
export { Vector2, Vector3 };
export {}
構文を使用すると、複数の識別子をエクスポートできます。 この場合、単一のexport
宣言を使用して、Vector2
クラスとVector3
クラスをエクスポートしています。
次のように、2つの別々のexport
ステートメントを使用することもできます。
import { Vector2 } from "./vector2";
import { Vector3 } from "./vector3";
export { Vector2 };
export { Vector3 };
これは、前のコードスニペットと同じ意味です。
src/vectors.ts
は2つのクラスのみをインポートして後で再エクスポートするため、構文のさらに短い形式を使用できます。
export { Vector2 } from "./vector2";
export { Vector3 } from "./vector3";
import
ステートメントはここでは暗黙的であり、TypeScriptコンパイラーはそれを自動的に含めます。 その後、ファイルはすぐに同じ名前でエクスポートされます。 このファイルを保存します。
src/vectors.ts
ファイルから2つのベクトルクラスをエクスポートしているので、src/index.ts
という名前の新しいファイルを作成し、ファイルを開いて次のコードを記述します。
import { Vector2, Vector3 } from "./vectors";
const vec2a = new Vector2(1, 2);
const vec2b = new Vector2(2, 1);
console.log(vec2a.add(vec2b));
const vec3a = new Vector3(1, 2, 3);
const vec3b = new Vector3(3, 2, 1);
console.log(vec3a.add(vec3b));
このコードでは、相対パス./vectors
を使用するsrc/vectors.ts
ファイルから両方のベクトルクラスをインポートしています。 次に、add
メソッドを使用してそれらを合計し、結果をログに記録して、いくつかのベクターインスタンスを作成します。
名前付きエクスポートをインポートする場合は、別のエイリアスを使用することもできます。これは、ファイル内での名前の衝突を回避するのに役立ちます。 これを試すには、ファイルに次の強調表示された変更を加えます。
import { Vector2 as Vec2, Vector3 as Vec3 } from "./vectors";
const vec2a = new Vec2(1, 2);
const vec2b = new Vec2(2, 1);
console.log(vec2a.add(vec2b));
const vec3a = new Vec3(1, 2, 3);
const vec3b = new Vec3(3, 2, 1);
console.log(vec3a.add(vec3b));
ここでは、as
キーワードを使用して、インポートされたクラスのエイリアスVec2
およびVec3
を設定しています。
./vectors
内で利用可能なすべてのものをインポートしていることに注意してください。 このファイルはこれら2つのクラスのみをエクスポートしているため、次の構文を使用してすべてを1つの変数にインポートできます。
import * as vectors from "./vectors";
const vec2a = new vectors.Vector2(1, 2);
const vec2b = new vectors.Vector2(2, 1);
console.log(vec2a.add(vec2b));
const vec3a = new vectors.Vector3(1, 2, 3);
const vec3b = new vectors.Vector3(3, 2, 1);
console.log(vec3a.add(vec3b));
上で強調表示されているコードでは、import * as
構文を使用して、モジュールによってエクスポートされるすべてのものを単一の変数にインポートします。 また、Vector2
クラスとVector3
クラスは、インポート中に作成されるvectors
オブジェクト内で使用できるようになったため、使用方法を変更する必要がありました。
ファイルを保存し、tsc
を使用してプロジェクトをコンパイルする場合:
- npx tsc
TypeScriptコンパイラはout/
ディレクトリを作成し(tsconfig.json
ファイルで設定したcompileOptions.outDir
オプションを指定)、ディレクトリにJavaScriptファイルを入力します。
out/index.js
で入手できるコンパイル済みファイルをお気に入りのテキストエディタで開きます。 次のようになります。
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vectors = require("./vectors");
const vec2a = new vectors.Vector2(1, 2);
const vec2b = new vectors.Vector2(2, 1);
console.log(vec2a.add(vec2b));
const vec3a = new vectors.Vector3(1, 2, 3);
const vec3b = new vectors.Vector3(3, 2, 1);
console.log(vec3a.add(vec3b));
tsconfig.json
ファイルのcompilerOptions.module
オプションがCommonJS
に設定されているため、TypeScriptコンパイラはNode.jsモジュールシステムと互換性のあるコードを作成します。 これは、require
関数を使用して、他のファイルをモジュールとしてロードします。
次に、out/vectors.js
で入手できるコンパイル済みのsrc/vectors.ts
ファイルを見てください。
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Vector3 = exports.Vector2 = void 0;
var vector2_1 = require("./vector2");
Object.defineProperty(exports, "Vector2", { enumerable: true, get: function () { return vector2_1.Vector2; } });
var vector3_1 = require("./vector3");
Object.defineProperty(exports, "Vector3", { enumerable: true, get: function () { return vector3_1.Vector3; } });
ここで、TypeScriptコンパイラは、CommonJSを使用するときにモジュールがエクスポートされる方法と互換性のあるコードを作成しました。これは、エクスポートされた値をexports
オブジェクトに割り当てます。
ファイルのインポートとエクスポートの構文を試し、JavaScriptにコンパイルされる方法を確認したので、ファイルでデフォルトのエクスポートを宣言することに進むことができます。
デフォルトのエクスポートの使用
このセクションでは、デフォルトのエクスポートと呼ばれるモジュールから値をエクスポートする別の方法を検討します。これにより、特定のエクスポートがモジュールからの想定されるインポートに設定されます。 これにより、ファイルをインポートするときにコードを簡略化できます。
src/vector2.ts
ファイルをもう一度開きます。
export class Vector2 {
constructor(public x: number, public y: number) {}
add(otherVector2: Vector2) {
return new Vector2(this.x + otherVector2.x, this.y + otherVector2.y);
}
}
ファイル/モジュールから単一の値をエクスポートする方法に注意してください。 エクスポートを作成するもう1つの方法は、デフォルトのエクスポートを使用することでした。 すべてのファイルは最大で1つのデフォルトのエクスポートを持つことができるので、これはここで便利です。
エクスポートをデフォルトのエクスポートに変更するには、次の強調表示されたコードを追加します。
export default class Vector2 {
constructor(public x: number, public y: number) {}
add(otherVector2: Vector2) {
return new Vector2(this.x + otherVector2.x, this.y + otherVector2.y);
}
}
ファイルを保存してから、src/vector3.ts
ファイルでも同じことを行います。
export default class Vector3 {
constructor(public x: number, public y: number, public z: number) {}
add(otherVector3: Vector3) {
return new Vector3(
this.x + otherVector3.x,
this.y + otherVector3.y,
this.z + otherVector3.z
);
}
}
デフォルトのエクスポートをインポートするには、ファイルを保存してから、src/vectors.ts
ファイルを開き、その内容を次のように変更します。
import Vector2 from "./vector2";
import Vector3 from "./vector3";
export { Vector2, Vector3 };
両方のインポートで、特定の値をインポートするためにdestructuringを使用する必要はなく、インポートに名前を付けるだけであることに注意してください。 これにより、各モジュールのデフォルトのエクスポートが自動的にインポートされます。
デフォルトのエクスポートがあるすべてのモジュールには、default
と呼ばれる特別なエクスポートもあり、これを使用してデフォルトのエクスポート値にアクセスできます。 以前に使用していたexport ... from
の省略構文を使用するには、次の名前のエクスポートを使用できます。
export { default as Vector2 } from "./vector2";
export { default as Vector3 } from "./vector3";
これで、各モジュールのデフォルトのエクスポートを特定の名前で再エクスポートします。
互換性のためにexport =
とimport = require()
を使用する
AMDやCommonJSなどの一部のモジュールローダーには、モジュールによってエクスポートされたすべての値を含むexports
というオブジェクトがあります。 これらのモジュールローダーのいずれかを使用する場合、exports
オブジェクトの値を変更することにより、エクスポートされたオブジェクトを上書きすることができます。 これは、ESモジュールで利用可能なデフォルトのエクスポートに似ているため、TypeScript自体でも利用できます。 ただし、これら2つの構文には互換性がありません。 このセクションでは、TypeScriptがデフォルトのエクスポートと互換性のある方法でこの動作を処理する方法を見ていきます。
TypeScriptでは、エクスポートされたオブジェクトの上書きをサポートするモジュールローダーをターゲットにする場合、export =
構文を使用してエクスポートされたオブジェクトの値を変更できます。 これを行うには、エクスポートされたオブジェクトの値をexport
識別子に割り当てます。 過去にNode.jsを使用したことがある場合、これはexports =
を使用した場合と同じです。
注:次の変更を行う前に、tsconfig.json
ファイルのオプションcompilerOptions.module
がCommonJS
に設定されていることを確認してください。
各ベクターファイルでエクスポートされたオブジェクトを変更して、ベクタークラス自体を指すようにするとします。 ファイルsrc/vector2.ts
を開きます。 エクスポートされたオブジェクト自体の値を変更するには、次の強調表示された変更を行います。
export = class Vector2 {
constructor(public x: number, public y: number) {}
add(otherVector2: Vector2) {
return new Vector2(this.x + otherVector2.x, this.y + otherVector2.y);
}
};
これを保存してから、ファイルsrc/vector3.ts
に対して同じことを行います。
export = class Vector3 {
constructor(public x: number, public y: number, public z: number) {}
add(otherVector3: Vector3) {
return new Vector3(
this.x + otherVector3.x,
this.y + otherVector3.y,
this.z + otherVector3.z
);
}
};
最後に、vectors.ts
を次のように戻します。
import Vector2 from "./vector2";
import Vector3 from "./vector3";
export { Vector2, Vector3 };
これらのファイルを保存してから、TypeScriptコンパイラを実行します。
- npx tsc
TypeScriptコンパイラでは、次のような複数のエラーが発生します。
Outputsrc/vectors.ts:1:8 - error TS1259: Module '"~/project/src/vector2"' can only be default-imported using the 'esModuleInterop' flag
1 import Vector2 from "./vector2";
~~~~~~~
src/vector2.ts:1:1
1 export = class Vector2 {
~~~~~~~~~~~~~~~~~~~~~~~~
2 constructor(public x: number, public y: number) {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
6 }
~~~
7 }
~
This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.
src/vectors.ts:2:8 - error TS1259: Module '"~/project/src/vector3"' can only be default-imported using the 'esModuleInterop' flag
2 import Vector3 from "./vector3";
~~~~~~~
src/vector3.ts:1:1
1 export = class Vector3 {
~~~~~~~~~~~~~~~~~~~~~~~~
2 constructor(public x: number, public y: number, public z: number) {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
10 }
~~~
11 }
~
This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.
このエラーは、src/vectors.ts
内でベクターファイルをインポートする方法が原因です。 このファイルは、モジュールのデフォルトのエクスポートをインポートするための構文を引き続き使用していますが、エクスポートされたオブジェクトを上書きしているため、デフォルトのエクスポートはもうありません。 両方の構文を一緒に使用することは互換性がありません。
これを解決するには、2つの方法があります。import = require()
を使用する方法と、TypeScriptコンパイラ構成ファイルでesModuleInterop
プロパティをtrue
に設定する方法です。
まず、src/vectors.ts
ファイルのコードに次の強調表示された変更を加えることにより、この種のモジュールをインポートするための正しい構文を試します。
import Vector2 = require("./vector2");
import Vector3 = require("./vector3");
export { Vector2, Vector3 };
import = require()
構文は、インポート自体の値としてexports
オブジェクトを使用し、各ベクトルクラスを使用できるようにします。 これで、コードのコンパイルは期待どおりに機能します。
TypeScriptエラー1259
を解決する別の方法は、tsconfig.json
ファイルでオプションcompilerOptions.esModuleInterop
をtrue
に設定することです。 デフォルトでは、この値はfalse
です。 これがtrue
に設定されている場合、TypeScriptコンパイラは追加のJavaScriptを発行し、エクスポートされたオブジェクトをチェックして、デフォルトのエクスポートであるか、上書きされたexports
オブジェクトであるかを検出し、それに応じて使用します。
これは期待どおりに機能し、以前と同じようにコードをsrc/vectors.ts
内に保持できます。 esModuleInterop
の動作と、出力されたJavaScriptコードに加えられる変更の詳細については、esModuleInteropオプションのTypeScriptドキュメントを確認してください。
注:このセクションで行われたすべての変更は、モジュールのエクスポートされたオブジェクトの上書きをサポートするモジュールシステムをターゲットにしていることを前提としています。 現在、これらのモジュールはAMDとCommonJSです。 別のモジュールシステムをターゲットにしている場合、TypeScriptコンパイラはコンパイル中にエラーを出します。
たとえば、ESモジュールシステムをターゲットにするためにcompilerOptions.module
構成がES6
に設定されている場合、TypeScriptコンパイラは複数のエラーを生成します。 X204X]および1203
:
"Output"src/vector2.ts:1:1 - error TS1203: Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.
1 export = class Vector2 {
~~~~~~~~~~~~~~~~~~~~~~~~
2 constructor(public x: number, public y: number) {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
6 }
~~~
7 };
~~
src/vectors.ts:1:1 - error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
1 import Vector2 = require("./vector2");
ESモジュールシステムのターゲット設定の詳細については、モジュールプロパティのTypeScriptコンパイラのドキュメントを参照してください。
結論
TypeScriptは、ESモジュール仕様に触発された構文を備えたフル機能のモジュールシステムを提供し、開発者は、 CommonJS 、 AMD など、発行されたJavaScriptコード内の他のさまざまなモジュールシステムをターゲットにできます。 ]、 UMD 、 SystemJS 、およびES6。 TypeScriptで使用可能なimport
およびexport
オプションを使用することにより、コードがモジュール式であり、より優れたJavaScript環境と互換性があることを確認できます。 モジュールは、拡張と保守が容易な適切に構造化されたコードベースを持つための基本的な部分であるため、モジュールの使用方法を知っていると、アプリケーションコードを簡潔かつ効率的に整理できます。
TypeScriptのその他のチュートリアルについては、TypeScriptシリーズのコーディング方法ページをご覧ください。