TypeScriptチートシート

前回: [ES6(ES2015)チートシート](http://qiita.com/morrr/items/883cb902ccda37e840bc)

ES6に続いて、altJSの中でもひときわ勢いを感じるTypeScriptについて調べてみました。


概要
------------------------------------------------------------

TypeScript = altJSの一種

- オープンソース / マイクロソフト発

- 構文はJavaScript(ES5-6)と上位互換性がある
    - ES6の構文はすべて使える

- 静的型付けによる型チェックができる点が特徴
    - その他、interface/enum/genericなど独自の機能が追加されている

- ES6と同様、開発時にjs(ES5)にトランスパイルすることで、ブラウザ上で実行できるようになる
    - トランスパイルはnpmパッケージ(`typescript`)を使う


導入
------------------------------------------------------------

### 試す(Playground)

ブラウザ上でTypeScriptを書いて動かせるPlaygroundが公開されている。

[http://www.typescriptlang.org/play/](http://www.typescriptlang.org/play/)


### 単独利用する(CLI)

TypeScriptファイルを手動でトランスパイルしたい場合は、npmパッケージをグローバルインストールした上でコマンド操作して使う。

```bash
# インストールする
$ yarn global add typescript

# コマンドラインでコンパイルする
# path/to/source.js が作られる
$ tsc path/to/source.ts
```

### バンドルツールに組み込む(webpack)

Webpackによるバンドル作成時に自動的にトランスパイルさせたい場合は、
TypeScript本体に加えてローダー`ts-loader`をインストール/設定する。

```bash
# webpack環境を構築済みのローカルにインストールする
$ cd path/to/proj
$ yarn add --dev typescript ts-loader

# デフォルトではプロジェクト直下にtsconfigファイルが必要になるので、空のファイルを作っておく
# あるいはtsc --init でテンプレートを配置しておく
$ touch ./tsconfig.json
```

```js:webpack.config.js
module.exports = {
  // ...

  module: {
    rules: [
      // 追加: TypeScript用のローダ追加
      {
        test: /\.ts$/,
        use: [{
          loader: 'ts-loader',
        }],
      },

      // ...
    ],
  },

  // ...
}
```


TypeScript独自の構文/機能
------------------------------------------------------------

### 型

ES5のプリミティブを拡張する形で、基本型が追加されている。


型              | 型指定時の記法            | リテラル                      | 備考
---             | ---                       | ---                           | ---
__any__         | `any`                     | -                             | プリミティブを含む任意の型を許容
__void__        | `void`                    | -                             | 戻り値なしを明示する場合など(null/undefinedは許容)
__null__        | `null`                    | `null`                        | nullのみ許容
__undefined__   | `undefined`               | `undefined`                   | undefinedのみ許容
__never__       | `never`                   | -                             | 到達しえない戻り値の型として形式的に設定する型
真偽値          | `boolean`                 | `true, false`                 | -
数値            | `number`                  | `123, 12.3, 0xFFFF, 0b1101`   | -
文字列          | `string`                  | `'string'`                    | -
配列            | `type[]`, `Array<type>`   | `[0, 1, 2]`                   | -
__タプル__      | `[type1, type2...]`       | `[0, 'value']`                | -
__enum__        | `SomeEnumType`            | `SomeEnumType.some`           | -


あらゆるクラスはnull(undefined)のサブタイプとして扱われる。
そのため、あらゆる型の変数は__null代入を許容する__。



#### 型指定(:type)

TypeScriptでは、変数や関数の引数、戻り値などに型を指定できる。
コンパイル時に静的な型チェックが行われ、問題がある場合はコンパイルエラーとして出力される。

ただし、コンパイルエラーになっても`.js`ファイルは作成__される__(でも動く保証はない)。

```ts:変数の型を指定する
let some: string ='foo' 
some = 123
//=> error TS2322: Type '123' is not assignable to type 'string'.
```

```ts:引数の型を指定する
// 引数を文字列型に指定
const echo = (message: string) => {
    console.log(message)
}

// OK
echo('test')

// コンパイル時にエラーになる
echo(123)
//=> error TS2345: Argument of type '123' is not assignable to parameter of type 'string'.
```


#### 型アサーション(as)

他の言語のキャストに近似する。
anyに変換することによってあらゆる代入時エラーを抑制(無視)させることもできる。

```ts
// 無理やり違う型の値をつっこむ
let some: number = 123
some = ('string' as any)
```

#### 型エイリアス(type)

型に別名をつけることができる。

```ts
class Foo{
}

// 別名をつける
type Bar = Foo


new Foo()   // OK
new Bar()   // NG: 型の参照以外の方法では利用できない


// OK: 引数や戻り値の型として利用するのは問題ない
function getBar(): Bar{
	return new Foo()
}
```

#### 共同型(|)

引数の型を復数指定できる。

```ts
// 引数のとりえる型を復数指定する
function foo(param: string | number){
	if(typeof param === 'string'){
		
	}else if (typeof param === 'number'){
		
	}
}
```



### インタフェース(interface)

Javaなどの他言語と同じインタフェースが定義できる。
またインタフェース内においても型の指定が可能。

```ts
interface Param{
    alpha: string,
    bravo: string,
}

const foo = (param: Param) => {
    console.log(param.alpha)
    console.log(param.bravo)
}

// OK
foo({
    alpha: 'test',
})

// NG
foo({
    charlie: 'test',
})
//=> error TS2345: Argument of type '{ charlie: string; }' is not assignable to parameter of type 'Param'.
//=>  Object literal may only specify known properties, and 'charlie' does not exist in type 'Param'.
```

#### オプションと読み専用指定(?/readonly)

インタフェースで定義したプロパティはデフォルトで必須となるが、`?`によって任意扱いにできる。
また、`readonly`によって変更を禁止できる。

```ts
interface Param{
    readonly alpha: string,
    bravo? : string,
}

let param: Param = { alpha: 'string' }
param.alpha = 'mod' // error
```

#### 関数のインタフェース定義

関数のシグネチャもインタフェースとして定義できる。

```ts
interface SomeFunc{
    (a: string, b: string): boolean
}

const someFunc: SomeFunc = (a, b, c) => {   // error(c)
    return 'bad return'                     // error
}
```

#### インタフェースの実装

クラスに適用することもできる。

```ts
interface SomeBehavior{
    // ...
}

class SomeClass implements SomeBehavior{
    // ...
}
```

### クラス

#### アクセス修飾子(public/private/protected)

プロパティのアクセス範囲を指定できる。
TypeScriptにおけるデフォルトは__public__。


```ts
class Foo{
	someDefault(){
	}
	public somePublic(){
	}
	private somePrivate(){
	}
	protected someProtected(){
	}
}

class Bar extends Foo{
	some(){
		super.someDefault()     // OK
		super.somePublic()      // OK
		super.somePrivate()     // NG
		super.someProtected()   // OK
	}
}

const foo = new Foo()
foo.someDefault()   // OK
foo.somePublic()    // OK
foo.somePrivate()   // NG
foo.someProteted()  // NG
```

#### 読み専用指定(readonly)

interfaceと同様に、プロパティを読み込み専用指定できる。

```ts
class Foo{
	readonly bar: string = 'BAR'
	baz: string = 'BAZ'
}

const foo = new Foo()
foo.bar             // OK
foo.baz             // OK
foo.bar = 'MOD'     // NG
foo.baz = 'MOD'     // OK
```

#### アクセサ(get/set)

外部からのプロパティの取得/設定をハンドルできる。

```ts
class Foo{
	private _param: string = null

	get param(): string{
		// ... プロパティの更新に加えて任意の処理を追加できる
		return this._param
	}

	set param(param: string){
		// ...
		this._param = param
	}
}

const foo = new Foo()
foo.param = 'foobar'
console.log(foo.param)
```

#### 抽象クラス(abstract)

抽象クラスを定義できる。
おおむね他言語と同じで、

- 抽象クラスをインスタンス化することは不可
- 抽象クラス内に具象メソッドを実装することもできる

```ts
abstract class Foo{             // 抽象クラスの定義
	abstract absMethod(): void      // 抽象メソッドの宣言
	nonAbsMethod(){
	}
}

class Bar extends Foo{
	absMethod(){
	}
}


const foo = new Foo()   // NG

const bar = new Bar()   // OK
bar.absMethod()
bar.nonAbsMethod()
```

### 関数/メソッド

#### シグネチャのチェック

型と同様に、引数の個数についてもコンパイル時にチェックされる。

```ts
const echo = () => {
    console.log(message)
}

echo('some message')
// => error TS2346: Supplied parameters do not match any signature of call target.
```


#### オプション引数(?)

必須でない引数はオプション指定することで、省略時のエラーを回避できる。

```ts
function required(some: string){
	return some
}

function optional(some?: string){
	return some
}

required('foo')     // OK
required()          // NG
optional('foo')     // OK
optional()          // OK
```

#### オーバーロード


引数の型ごとに、異なる関数を定義できる。

ひとつの引数に対して復数のシグネチャを設定できる。

```ts
function foo(param: string)
function foo(param: number)
function foo(param: any){
	switch(typeof param){
		case 'string':
		case 'number':
		// ...
	}
}

foo('string')
foo(123)
```


### ジェネリック型(Generics)

ジェネリック(テンプレート)型の関数を定義できる。

```ts
function echo<T>(param: T): T{
	return param
}

echo('test')
echo(123)
```

クラスに対しても指定できる。

```ts
class SomeClass<T>{
	someParam: T
	someMethod(param: T): T{
		// ...
	}
}

const strInstance = new SomeClass<string>()
const numInstance = new SomeClass<number>()
```

### 列挙型(Enum)

列挙型を定義できる。

```ts
enum ABC {
    A = 1,          // 値の指定が可能
    B = 1<<4,       // 式により値を指定することも可能
    C = 1 + 3,
    D,              // 未指定の場合は前の値+1
}
```


### 名前空間(namespace)

モジュールの概念に近似する(`internal module`と呼ばれていたとのこと)。
1モジュール内に名前空間を定義し、スコープを制限したり、実装を隠蔽できる。

```ts
namespace Foo{
    // exportしないと外部からアクセスできない
	export const bar = 'BAR'
	const baz = 'BAZ'

    // ネストすることも可
    export namespace Hoge{
        export const piyo = 'PIYO'
    }
}

Foo.bar     // OK
bar         // NG: 空間が異なる
Foo.baz     // NG: exportされていない

// 別名をつける
Foo.Hoge.piyo   // OK

import Hoge = Foo.Hoge
Hoge.piyo       // OK
```


### 宣言のマージ

namespace, interface, class, enumを重複して定義すると、その内容が自動的にマージされる。

```ts
namespace Foo{
	export const bar = 'BAR'
}

// 重複する名前空間を定義
namespace Foo{
	export const baz = 'BAZ'
}

// お互いが上書きされることなく、両方の機能が生きる
Foo.bar
Foo.baz
```

### JSX構文

Reactで利用されるJSX構文をサポートしている。
利用するためには以下を満たす必要がある:

- ファイル名を`*.tsx`とすること
- コンパイル時に`jsx`オプションを有効にすること



設定ファイル(tsconfig.json)
------------------------------------------------------------

TypeScriptのコンパイルに関する設定をファイル化できる。
設定ファイルは`tsconfig.json`というファイル名で、プロジェクトのルートに配置する。


### 設定ファイルの作成

CLIの`init`オプションを使うとテンプレートファイルが作成されるので、これをベースに編集する。

```bash
# カレントにtsconfig.jsonのテンプレートが配置される
$ tsc --init
```

`compilerOptions`のオプション名は、tscコマンドのオプションと共通する。
オプションの一覧は[公式](https://www.typescriptlang.org/docs/handbook/compiler-options.html)を参照のこと。

設定ファイルにおける設定項目は__すべて任意__なので、デフォルトを使う場合は省略してもいい。


```js:tsconfig.json(抜粋)
{
    // コンパイラオプションを指定
    "compilerOptions": {
        "target": "ES5",                // ES5に変換する
        "module": "ES6",                // ES6方式のモジュールシステム(import/export)を使う
        "removeComments": true,         // 変換時にコメントを消す
        "outFile": "path/to/dest.js",   // 指定した出力先にファイルをバンドルする
        "sourceMap": true               // デバッグ用のmapファイルを生成
        "typeRoots": [ ... ],           // 型定義ファイルを明示
    },

    // コンパイル対象のファイルを指定
    "files": [
        "path/to/src/moduleA.ts",
        "path/to/src/moduleB.ts",
        "path/to/src/moduleC.ts",
        "path/to/src/entry.ts",
    ]

    // あるいは、対象ファイル/除外ファイルの形式で指定することも可
    "include": [
        "path/to/src/**/*"          // 対象フォルダ以下をまとめて追加
    ],
    "exclude": [
        "path/to/src/**/*.spec.ts"  // テストファイルは除外
    ]
}
```

### 設定ファイルの利用

CLIの場合、設定ファイルは`project`オプションで指定できる。

```bash
# 設定ファイルの内容でコンパイル実行
$ tsc --project ./tsconfig.json
```



ネイティブjsとの連携(型定義ファイル, .d.ts)
------------------------------------------------------------

型定義ファイル = ネイティブJavaScriptで書かれたコードをTypeScriptが解釈するための型情報の定義ファイル。
CLIオプションあるいは設定ファイルで型定義ファイルを読み込むことで、TypeScript側からjsコードを利用できるようになる。

自作する他、有名なライブラリは公開された既存のファイルを使える。
たとえばnpmパッケージの場合は`@types/*`という名前で型定義ファイルが配信されている。

```bash:npmで型定義ファイルを探す
$ npm search @types

#> @types/node               | TypeScript…          | =types          | 2017-06-12 |          |        
#> @types/lodash             | TypeScript…          | =types          | 2017-06-12 |          | 
#> @types/jquery             | TypeScript…          | =types          | 2017-06-02 |          | 
#> @types/react              | TypeScript…          | =types          | 2017-06-12 |          | 
#> ...
```

```
型定義ファイルの作成については今回未調査です
```



参考
------------------------------------------------------------
- [Wikipedia - TypeScript](https://ja.wikipedia.org/wiki/TypeScript)
- [TypeScript](https://www.typescriptlang.org/)
    - [Docuentation](https://www.typescriptlang.org/docs/home.html)

- [TypeScript Ninja - Revised TypeScript in Definitelyland](http://typescript.ninja/typescript-in-definitelyland/index.html)
    - [第6章　JS資産と型定義ファイル](http://typescript.ninja/typescript-in-definitelyland/definition-file.html)

- [Qiita - React + TypeScript + Webpackの最小構成](http://qiita.com/uryyyyyyy/items/63969d6ed9341affdffb)
- [Build INSIDER - TypeScript早わかりチートシート【1.5.3対応】](http://www.buildinsider.net/language/quicktypescript/01)
- [@IT - TypeScriptの進化の道のりとバージョン2.0の新機能（型システム編） (1/3)](http://www.atmarkit.co.jp/ait/articles/1611/08/news029.html)
- [@IT - TypeScriptの進化の道のりとバージョン2.0の新機能（型システム編） (2/3)](http://www.atmarkit.co.jp/ait/articles/1611/08/news029_2.html)
- [@IT - TypeScriptの進化の道のりとバージョン2.0の新機能（型システム編） (3/3)](http://www.atmarkit.co.jp/ait/articles/1611/08/news029_3.html)