TypeScript Handbook を読む (14. Namespaces)

[TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) を読み進めていく第十四回目。

1. [Basic Types](http://qiita.com/murank/items/9c1f633545842c953947)
1. [Variable Declarations](http://qiita.com/murank/items/913f1c1e4e725047436c)
1. [Interfaces](http://qiita.com/murank/items/553276c3dc2eec9bfe47)
1. [Classes](http://qiita.com/murank/items/7e8f15225bc8259e9bf3)
1. [Functions](http://qiita.com/murank/items/e310ce68ed5a0058d42d)
1. [Generics](http://qiita.com/murank/items/144b686d34fd433a97a3)
1. [Enums](http://qiita.com/murank/items/cc487562b21bae3728ba)
1. [Type Inference](http://qiita.com/murank/items/e67ad7426020321228e9)
1. [Type Compatibility](http://qiita.com/murank/items/e06efb0615294a6826e3)
1. [Advanced Types](http://qiita.com/murank/items/bc1776efbecbd2120d94)
1. [Symbols](http://qiita.com/murank/items/04b3b31caa677750f240)
1. [Iterators and Generators](http://qiita.com/murank/items/7ceec53dde886f31a682)
1. [Modules](http://qiita.com/murank/items/0fc4e059be8ea5e1c274)
1. Namespaces (今ココ)
1. [Namespaces and Modules](http://qiita.com/murank/items/0be1978a3515fc76e7ba)
1. [Module Resolution](http://qiita.com/murank/items/5f11466474d49498bbd5)
1. [Declaration Merging](http://qiita.com/murank/items/ec90ba04edab1ea3718e)
1. [JSX](http://qiita.com/murank/items/90164aa9a3bed3c49a80)
1. [Decorators](https://qiita.com/murank/items/580e34883e0832ca7aed)
1. [Mixins](https://qiita.com/murank/items/e77ad8359ed1fd82822d)
1. [Triple-Slash Directives](https://qiita.com/murank/items/faff57883338ebdeda12)
1. [Type Checking JavaScript Files](https://qiita.com/murank/items/588eaddfe8af59914658)


# Namespaces
[原文](https://www.typescriptlang.org/docs/handbook/namespaces.html)

## First steps
コード例として、ユーザ入力や外部ファイルをバリデーションするための簡単な文字列バリデータを使用します。

```ts:バリデータ（単一ファイルの場合）
interface StringValidator {
    isAcceptable(s: string): boolean;
}

let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = /^[0-9]+$/;

class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

// チェック対象
let strings = ["Hello", "98052", "101"];

// 使用するバリデータ
let validators: { [s: string]: StringValidator; } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();

// 各文字列がバリデーションを通過したかどうかを表示する
for (let s of strings) {
    for (let name in validators) {
        let isMatch = validators[name].isAcceptable(s);
        console.log(`'${ s }' ${ isMatch ? "matches" : "does not match" } '${ name }'.`);
    }
}
```


## Namespacing
バリデータを追加するにつれて、バリデータの管理や名前の衝突の回避のためになんらかの方法でバリデータを整理したいと思うかもしれません。
その場合、個別に名前を付けてグローバル名前空間に配置する代わりに、オブジェクトを名前空間でラップしてみましょう。

先ほどの例について、バリデータに関係するコードを `Validation` に移動し、外部に公開するインタフェース/クラスに `export` を付与します。
逆に、`lettersRegexp`、`numberRegexp` は実装の詳細であるため、エクスポートしないようにします。
名前空間の外からバリデータを使用するためには、名前空間名付きで指定する必要があります。(例: `Validation.LettersOnlyValidator`)

```ts:バリデータ(名前空間あり)
namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }

    const lettersRegexp = /^[A-Za-z]+$/;
    const numberRegexp = /^[0-9]+$/;

    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }

    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}

// チェック対象
let strings = ["Hello", "98052", "101"];

// 使用するバリデータ
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

// 各文字列がバリデーションを通過したかどうかを表示する
for (let s of strings) {
    for (let name in validators) {
        console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
    }
}
```


## Splitting Across Files
### Multi-file namespaces
次に、`Validation` 名前空間を複数のファイルに分割してみましょう。
ファイルが分かれていても、参照タグを基にそれらの間の依存関係を解決するため、あたかも一箇所で定義されているかのように扱うことが可能です。

```ts:Validation.ts
namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}
```
```ts:LettersOnlyValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}
```
```ts:ZipCodeValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
    const numberRegexp = /^[0-9]+$/;
    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}
```
```ts:Test.ts
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />

// チェック対象
let strings = ["Hello", "98052", "101"];

// 使用するバリデータ
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

// 各文字列がバリデーションを通過したかどうかを表示する
for (let s of strings) {
    for (let name in validators) {
        console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
    }
}
```

分割したファイルはすべて読み込まれるようにしないといけませんが、そのためには 2 種類の方法があります。

ひとつ目の方法は、`--outFile` フラグを使用してコンパイル時にひとつの JavaScript ファイルにまとめることです。

```bash:コマンド
tsc --outFile sample.js Test.ts
```

出力ファイル中における各ファイルの順番は参照タグを基に自動的に判定されますが、以下のように個別にファイルを指定することも可能です。

```bash:コマンド
tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
```

それ以外の方法として、各ファイルを個別の JavaScript ファイルに出力 (デフォルト) することも可能です。
その場合、`<script>` タグを使用し、適切な順番で JavaScript ファイルを読み込む必要があります。

```html:MyTestPage.html（上級者向け）
    <script src="Validation.js" type="text/javascript" />
    <script src="LettersOnlyValidator.js" type="text/javascript" />
    <script src="ZipCodeValidator.js" type="text/javascript" />
    <script src="Test.js" type="text/javascript" />
```

## Aliases
名前空間を簡単に取り扱う別の方法として、`import q = x.y.z` を使用して短縮名を作ることが可能です。

```ts:TypeScript
namespace Shapes {
    export namespace Polygons {
        export class Triangle { }
        export class Square { }
    }
}

import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // 'new Shapes.Polygons.Square()' と同等
```

ここで `require` を使用する代わりに、シンボルの修飾名を直接代入していることに注意してください。
`var` を使用した変数宣言に似ていますが、型や名前空間にも使用することができます。
重要なポイントとして、値に対して `import` を使用した場合、元のオブジェクトとは区別して扱われるため、値を変更しても元の値には反映されません。


## Working with Other JavaScript Libraries
TypeScript で書かれていないライブラリについては、そのライブラリの提供する API を宣言する必要があります。
この実装を伴わない宣言のことを "ambient" と呼んでいます。

### Ambient Namespaces
有名なライブラリである D3 は `d3` というグローバルオブジェクトを提供しています。
このライブラリは (モジュールローダーを使用せずに) `<script>` タグ経由で読み込まれるため、ライブラリの宣言に名前空間を使用しています。
TypeScript からこの宣言を参照するには、アンビエント名前空間を使用します。

```ts:D3.d.ts（上級者向け簡易版）
declare namespace D3 {
    export interface Selectors {
        select: {
            (selector: string): Selection;
            (element: EventTarget): Selection;
        };
    }

    export interface Event {
        x: number;
        y: number;
    }

    export interface Base extends Selectors {
        event: Event;
    }
}

declare var d3: D3.Base;
```
>`declare 〜` がアンビエント宣言部分