著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

Modules は、コードをより小さく、より管理しやすい部分に編成する方法であり、プログラムがアプリケーションのさまざまな部分からコードをインポートできるようにします。 何年にもわたって、JavaScriptコードにモジュール性を実装するためのいくつかの戦略がありました。 TypeScript は、 ECMAScript 仕様とともに進化し、これらのさまざまな形式を柔軟に処理できるJavaScriptプログラムの標準モジュールシステムを提供します。 TypeScriptは、 ESモジュール構文と同様の統一構文でモジュールを作成および使用するためのサポートを提供し、開発者はNode.js( CommonJS [ X238X])、require.js( AMD )、 UMD SystemJS 、または ECMAScript 2015ネイティブモジュール(ES6)。

このチュートリアルでは、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つの小さなクラスVector2Vector3を含むサンプルプロジェクトを作成します。 この場合のvectorは、視覚的なグラフィックプログラムでよく使用される、大きさと距離の数学的測定値を指します。

作成するクラスには、ベクトルの加算という1つの操作があります。 後で、これらのサンプルクラスを使用して、あるプログラムから別のプログラムへのコードのインポートとエクスポートをテストします。

まず、サンプルコードを格納するディレクトリを作成します。

  1. mkdir vector_project

ディレクトリが作成されたら、それを作業ディレクトリにします。

  1. cd vector_project

プロジェクトのルートになったら、npmを使用してNode.jsアプリを作成します。

  1. npm init

これにより、プロジェクトのpackage.jsonファイルが作成されます。

次に、TypeScriptを開発依存関係として追加します。

  1. npm install [email protected] --save-dev

これにより、TypeScriptコンパイラがデフォルト設定に設定された状態で、TypeScriptがプロジェクトにインストールされます。 独自のカスタム設定を行うには、特定の構成ファイルを作成する必要があります。

プロジェクトのルートにtsconfig.jsonという名前のファイルを作成して開きます。 このチュートリアルの演習でプロジェクトを機能させるには、次の内容をファイルに追加します。

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/という名前の新しいディレクトリを作成します。

  1. mkdir src

これは、tsconfig.jsonファイルでルートディレクトリ(rootDir)として設定したディレクトリです。

このフォルダ内に、vector2.tsという名前の新しいファイルを作成します。 このファイルをお気に入りのテキストエディタで開き、Vector2クラスを記述します。

vector_project / src / vector2.ts
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クラスをエクスポートする必要があります。

vector_project / 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/vector2.tsは、Vector2クラスという単一のエクスポートを持つモジュールになりました。 ファイルを保存して閉じます。

次に、Vector3クラスを作成できます。 src/ディレクトリ内にvector3.tsファイルを作成し、お気に入りのテキストエディタでファイルを開いて、次のコードを記述します。

vector_project / 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.tsvector3.tsの2つのファイルができました。どちらもモジュールです。 各ファイルには、それらが表すベクトルのクラスである単一のエクスポートがあります。 次のセクションでは、importステートメントを使用してこれらのモジュールを他のファイルに取り込みます。

importを使用したTypeScriptでのモジュールの使用

前のセクションでは、モジュールを作成する方法を見ました。 このセクションでは、これらのモジュールをインポートして、コードの他の場所で使用します。

TypeScriptでモジュールを操作する場合の一般的なシナリオは、複数のモジュールを収集し、それらを1つのモジュールとして再エクスポートする単一のファイルを作成することです。 これを示すために、src/ディレクトリ内にvectors.tsというファイルを作成し、このファイルをお気に入りのエディターで開いて、次のように記述します。

vector_project / src / vectors.ts
import { Vector2 } from "./vector2";
import { Vector3 } from "./vector3";

プロジェクトで使用可能な別のモジュールをインポートするには、importステートメントでファイルへの相対パスを使用します。 この場合、現在のファイルからファイルsrc/vector2.tsおよびsrc/vector3.tsへの相対パスである./vector2および./vector3から両方のモジュールをインポートしています。

ベクトルをインポートしたので、次の強調表示された構文を使用して、単一のモジュールでそれらを再エクスポートできます。

vector_project / src / vectors.ts
import { Vector2 } from "./vector2";
import { Vector3 } from "./vector3";

export { Vector2, Vector3 };

export {}構文を使用すると、複数の識別子をエクスポートできます。 この場合、単一のexport宣言を使用して、Vector2クラスとVector3クラスをエクスポートしています。

次のように、2つの別々のexportステートメントを使用することもできます。

vector_project / src / vectors.ts
import { Vector2 } from "./vector2";
import { Vector3 } from "./vector3";

export { Vector2 };
export { Vector3 };

これは、前のコードスニペットと同じ意味です。

src/vectors.tsは2つのクラスのみをインポートして後で再エクスポートするため、構文のさらに短い形式を使用できます。

vector_project / src / vectors.ts
export { Vector2 } from "./vector2";
export { Vector3 } from "./vector3";

importステートメントはここでは暗黙的であり、TypeScriptコンパイラーはそれを自動的に含めます。 その後、ファイルはすぐに同じ名前でエクスポートされます。 このファイルを保存します。

src/vectors.tsファイルから2つのベクトルクラスをエクスポートしているので、src/index.tsという名前の新しいファイルを作成し、ファイルを開いて次のコードを記述します。

vector_project / 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メソッドを使用してそれらを合計し、結果をログに記録して、いくつかのベクターインスタンスを作成します。

名前付きエクスポートをインポートする場合は、別のエイリアスを使用することもできます。これは、ファイル内での名前の衝突を回避するのに役立ちます。 これを試すには、ファイルに次の強調表示された変更を加えます。

vector_project / src / index.ts
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つの変数にインポートできます。

vector_project / src / index.ts
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を使用してプロジェクトをコンパイルする場合:

  1. npx tsc

TypeScriptコンパイラはout/ディレクトリを作成し(tsconfig.jsonファイルで設定したcompileOptions.outDirオプションを指定)、ディレクトリにJavaScriptファイルを入力します。

out/index.jsで入手できるコンパイル済みファイルをお気に入りのテキストエディタで開きます。 次のようになります。

vector_project / 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ファイルを見てください。

vector_project / out / vectors.js
"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ファイルをもう一度開きます。

vector_project / 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つのデフォルトのエクスポートを持つことができるので、これはここで便利です。

エクスポートをデフォルトのエクスポートに変更するには、次の強調表示されたコードを追加します。

vector_project / src / vector2.ts
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ファイルでも同じことを行います。

vector_project / 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ファイルを開き、その内容を次のように変更します。

vector_project / src / vectors.ts
import Vector2 from "./vector2";
import Vector3 from "./vector3";

export { Vector2, Vector3 };

両方のインポートで、特定の値をインポートするためにdestructuringを使用する必要はなく、インポートに名前を付けるだけであることに注意してください。 これにより、各モジュールのデフォルトのエクスポートが自動的にインポートされます。

デフォルトのエクスポートがあるすべてのモジュールには、defaultと呼ばれる特別なエクスポートもあり、これを使用してデフォルトのエクスポート値にアクセスできます。 以前に使用していたexport ... fromの省略構文を使用するには、次の名前のエクスポートを使用できます。

vector_project / src / vectors.ts
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.moduleCommonJSに設定されていることを確認してください。

各ベクターファイルでエクスポートされたオブジェクトを変更して、ベクタークラス自体を指すようにするとします。 ファイルsrc/vector2.tsを開きます。 エクスポートされたオブジェクト自体の値を変更するには、次の強調表示された変更を行います。

vector_project / 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に対して同じことを行います。

vector_project / 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を次のように戻します。

vector_project / src / vectors.ts
import Vector2 from "./vector2";
import Vector3 from "./vector3";

export { Vector2, Vector3 };

これらのファイルを保存してから、TypeScriptコンパイラを実行します。

  1. npx tsc

TypeScriptコンパイラでは、次のような複数のエラーが発生します。

Output
src/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ファイルのコードに次の強調表示された変更を加えることにより、この種のモジュールをインポートするための正しい構文を試します。

vector_project / src / vectors.ts
import Vector2 = require("./vector2");
import Vector3 = require("./vector3");

export { Vector2, Vector3 };

import = require()構文は、インポート自体の値としてexportsオブジェクトを使用し、各ベクトルクラスを使用できるようにします。 これで、コードのコンパイルは期待どおりに機能します。

TypeScriptエラー1259を解決する別の方法は、tsconfig.jsonファイルでオプションcompilerOptions.esModuleInteroptrueに設定することです。 デフォルトでは、この値は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シリーズのコーディング方法ページをご覧ください。