TypeScript (1.8) のモジュール・クラス・型定義の書き方

# 前提
- TypeScript 1.8

# 背景
TypeScriptの型定義やモジュール周りの書き方・設定はなんだかよくわからない部分が多いです。いくつかのサンプルコードを見るとノリで書けそうだけど、結局ハマってしまいました。

ハマってしまった部分を中心にざっくり、かつできるだけ具体的にまとめます。

英語が読める人はこの記事を読むよりも、ハンドブックの最初からじっくり読んだ方がいいと思います。

- [Basic Types · TypeScript](https://www.typescriptlang.org/docs/handbook/basic-types.html)

# クラスを定義して読み込む方法

## class

まず、普通にクラスを定義する書き方はこうです。

```ts
class Greeter {
    public hello() {
        console.log('hello');
    }
}
```

## exportとimport
では、他のファイルからimportして使いたいときにどう書けばいいでしょうか。こう書けばいいです。

```ts
export class Greeter {
    public hello() {
        console.log('hello');
    }
}
```

```ts
import {Greeter} from './hello-world';

let greeter = new Greeter();
greeter.hello(); // hello
```

## default
defaultというキーワードを使うとこんな感じに書けます。

```ts:hello-world.ts
export default class Greeter {
    public hello() {
        console.log('hello');
    }
}
```

```ts:use-class.ts
import MyGreeter from './hello-world';

let greeter = new MyGreeter();
greeter.hello(); // hello
```

defaultを指定してあげたので、先ほどのように「`hello-world.ts`からどれをimportするか」という指定（先の例だとGreeter）が不要になりました。

ここで、Greeterというクラスを使うときにMyGreeterという名前で使っていますので、先程の`import {Greeter}`とは意味が違うことが分かるかと思います。要は、以下と等しい。

```ts
import {Greeter} as MyGreeter from 'hello-world';
```

# モジュールを定義して読み込む方法
状態を持たない・インスタンスを作らない関数群をまとめておきたいだけの時もあるので、全てをクラスとして定義するわけにもいきません。Rubyで言うところの以下のようなものを書きたいと思いました。

```ruby:hello_world.rb
module HelloWorld
  extend self
  def hello
    puts :hello
  end
end
```

```use_module.rb
require "./hello_world"

HelloWorld.hello #=> :hello
```

## export function
`export function hoge() {}`のように関数群を書いて、それをimportすればいいです。気持ち的に`namespace`とか`module`で区切ってあげなきゃいけないんじゃないの...?と思いたくなりますが、そんなこと無いようです。

```ts:hello-world.ts
export function hello() {
    console.log('hello');
}
export function goodBye() {
    console.log('good bye.');
}
```

上のように書いて、以下のように使えます。

```ts:use-module.ts
import {hello} from './hello-world';

hello();
```

あるいは、モジュールごと全部読んでもいい。

```ts:use-module.ts
import * as HelloModule from './hello-world';
HelloModule.hello();
```

### モジュールの補足: TypeScript 1.8 以降 と use strict
`export function() {}`を書いたらTSLintに怒られました。

tslint.jsonを見ると、たしかにその設定が書かれていました。

```ts
"use-strict": [true, "check-module", "check-function"]
```

TypeScriptは1.8から use strictを自動で付加してくれるようになっているので、この設定は要りません（当該プロジェクトでこの設定が書かれたのは1.5.0beta時代でした）。

# TypeScriptの型定義
型定義にまつわる何かを書いていきます。

## 型定義ファイルとDefinitelyTyped
DefinitelyTypedというところにある型定義ファイルをインストールして使えば大丈夫です。

```ts
typings install dt~jquery --save --global
```

基本的にTypeScriptで Node Modules を使う場合、 npm install したら typings install することになるかと思います。TypeScript 2.0 からは`@types`というのを使うそうですが、よく知りません。

## interface Window の拡張
たとえばGoogle Analyticsのような外部スクリプトを使う場合、そのスクリプトの関数は`window`オブジェクトに生やされます。しかし、これを参照しようとするコードをTypeScriptで書くと怒られます。

じゃあどうすればいいのかというと、このインタフェースを拡張することになります。

```ts
declare global {
    interface Window {
        ga?: () => void;
    }
}
```

これで、`window.ga`を参照するコードを書いても怒られません。

___

まだよく理解してない部分があるような気がするので、書き足したり書き直したりしていきます。

# 参考
- [Modules · TypeScript](https://www.typescriptlang.org/docs/handbook/modules.html)
- [TypeScript の型定義ファイルと仲良くなろう - Hatena Developer Blog](http://developer.hatenastaff.com/entry/2016/06/27/140931)
- [\`use\-strict\` is \(partially?\) obsolete · Issue \#678 · palantir/tslint](https://github.com/palantir/tslint/issues/678)