LoginSignup
3
2

More than 5 years have passed since last update.

TypeScript Handbook を読む (15. Namespaces and Modules)

Last updated at Posted at 2017-06-11

TypeScript Handbook を読み進めていく第十五回目。

  1. Basic Types
  2. Variable Declarations
  3. Interfaces
  4. Classes
  5. Functions
  6. Generics
  7. Enums
  8. Type Inference
  9. Type Compatibility
  10. Advanced Types
  11. Symbols
  12. Iterators and Generators
  13. Modules
  14. Namwspaces
  15. Namespaces and Modules (今ココ)
  16. Module Resolution
  17. Declaration Merging
  18. JSX
  19. Decorators
  20. Mixins
  21. Triple-Slash Directives
  22. Type Checking JavaScript Files

Namespaces and Modules

原文

Using Namespaces

名前空間は単なる名前付きオブジェクトであり、複数ファイルにまたがる名前空間を --outFile で連結することが可能です。
名前空間は Web アプリーションを構築する際に <script> を使用して依存ファイルを読み込む場合に使用すると良いでしょう。

ただし、特に大規模なアプリケーションにおいて、コンポーネント間の依存関係を識別することは難しくなります。

Using Modules

モジュールは名前空間と同じように実装と宣言を含んでいますが、モジュールはそれ自身の依存関係を 宣言 している点が異なります。

このことは小規模なアプリケーションでは必ずしも最適とは限りませんが、アプリケーションが大規模であれば、長期間にわたるモジュール性やメンテナンス性において、充分コストに見合った利益が得られます。

モジュールは ECMAScript 2015 から言語規格にも組み込まれており、新しいプロジェクトではコードを体系化するための手法として推奨します。

Pitfalls of Namespaces and Modules

/// <reference>-ing a module

よくある間違いのひとつとして、モジュールファイルを参照するために import 構文ではなく、/// <reference ... /> を使用してしまうことが挙げられます。

なぜこれが間違いなのか理解するために、まずコンパイラがどのようにインポートパス (例: import x from "...";import x = require("...");... 部分) に基づいてモジュールの型情報を読み込むか見てみましょう。

コンパイラは指定されたパスに対し、.ts.tsx ファイルが存在するか確認した上で、適切なパスに .d.ts ファイルが存在するか確認します。
もしも該当するファイルが存在しなかった場合、アンビエントモジュール宣言 が検索されます。

myModules.d.ts
// .d.ts ファイル、またはモジュールではない .ts ファイルでの宣言
declare module "SomeModule" {
    export function fn(): string;
}
myOtherModule.ts
/// <reference path="myModules.d.ts" />
import * as m from "SomeModule";

参照タグを使用することで、アンビエントモジュールを宣言しているファイルを指定することが可能です。

SomeModule.tsSomeModule.d.ts を探しに行ってしまうため、参照タグで正しいパスを指定してるということかな?

Needless Namespacing

名前空間からモジュールを使用するようにプログラムを変更する場合、次のようなコードを書いてしまうかもしれません。

shapes.ts
export namespace Shapes {
    export class Triangle { /* ... */ }
    export class Square { /* ... */ }
}

ShapesTriangleSquare をラップしていますが、これは何の意味もない上に、利用者の混乱を招いてしまいます。

shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle(); // shapes.Shapes だって?

モジュールの利用者は読み込んだモジュールの名前を指定することが可能なため、エクスポートするシンボルをわざわざ名前空間でラップする必要はありません。

モジュールにおいて名前空間を使用するべきでない理由を繰り返すと、名前空間は名前の衝突を避けるために用いられるものですが、そもそもモジュールはインポート時に名前を指定するため、さらにレイヤーを増やす必要性はまったくないということです。

それを踏まえた正しいコードは以下のとおりです。

shapes.ts
export class Triangle { /* ... */ }
export class Square { /* ... */ }
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();

Trade-offs of Modules

JS ファイルとモジュールに 1 対 1 の関係があるように、TypeScript でもモジュールファイルと出力される JS ファイルに 1 対 1 の関係があります。
つまり、使用するモジュールローダーによっては、複数のモジュールファイルを連結できないということです。
例えば、commonjsumd を使用する場合には outFile オプションを使用することはできませんが、TypeScript 1.8 以降であれば amdsystem を使用することで outFile オプションを使用できます。

まあ実際には webpack とかで連結するからいいんですけどね

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2