TypeScriptの基本を振り返る

### 概要

いまさらながらTypeScriptの基本的なところ振り返りました。
やっぱり基本を何度も反復することは大事だと思う今日この頃。
備忘録的な感じで本記事に纏めます。

### TypeScriptとは

TypeScrtiptは**斬新的型付け言語**の一つ。
JavaScriptの開発を促進させてくれる。

例) JavaScriptの開発において...

```js:
// yamadaという姓を持つuserオブジェクトを定義した。
const user = { lastName: 'yamada' }
// yamadaからtanakaという姓に変える処理を開発者は強いられることになったが、間違えて名として代入してしまった!!
user.firstName = 'tanaka'
// 結果、userオブジェクトにfirstNameというプロパティを誤って追加してしまった...
console.log(user) // {lastName: "yamada", firstName: "tanaka"}
```
極端すぎるが、上例みたいな事が起きないように事前に阻止してくれる。
TypeScriptで開発を行っている場合、上例の場合だと、`user.firstName = 'tanaka'`の行で**userオブジェクトにfirstNameというプロパティは存在しない**というエラーを出してくれる。

### TypeScriptのセットアップ

TypeScriptを利用した開発を行う場合は、Node.jsが必要になるので事前にPCにインストールしておく。
Node.jsがPCにインストール出来たならば、下記コマンドでprojectディレクトリがTypeScript環境となる。

```sh:
$ cd project
$ npm install --save-dev typescript tslint @types/node
```

### tsconfig.json

TypeScriptのプロジェクトのルートには、`tsconfig.json`を設置しておく必要がある。
`tsconfig.json`に定義するオプションの意味は[ここ](https://typescript-jp.gitbook.io/deep-dive/project/compilation-context/tsconfig)が分かりやすい。
下記のように、`tsc`コマンドで`tsconfig.json`を生成することも可能。

```sh:
$ cd project
$ npm install --save-dev typescript tslint @types/node
$ ./node_modules/.bin/tsc --init       # make tsconfig.json
```

### tslint.json

一貫したコーディングルールスタイルを強制したい時に使用する。([tslintのDocuments](https://palantir.github.io/tslint/rules/))
下記のように、`tslint`コマンドで`tslint.json`を生成することが可能。

```sh:
$ ./node_modules/.bin/tslint --init 
```

### コンパイル＆実行

TypeScriptファイル ( 拡張子が`.ts`のファイル) を`./node_modules/.bin/tsc`コマンドでコンパイルするとJavaScriptファイルが生成される。
生成されたJavaScriptはNode環境であれば`node hoge.js`のように実行する。
別の方法として、[ts-node](https://www.npmjs.com/package/ts-node)を別途プロジェクトにインストールして`ts-node hoge.ts`のように一回のコマンドでコンパイルおよび実行も可能。
`ts-node`でコンパイル&実行した場合は、**JavaScriptファイルは生成されない。**

```sh:
$ cd project
$ npm install --save-dev ts-node
$ vi index.ts
console.log('hello !!'); 
$ ./node_modules/.bin/ts-node index.ts
hello !!
```

Microsoftが用意している[スキャフォールディングツール](https://github.com/Microsoft/TypeScript-Node-Starter.git)を利用すると即座にTypeScriptのプロジェクトを構築できる。

```sh
$ git clone https://github.com/Microsoft/TypeScript-Node-Starter.git project
$ cd project
$ npm install
```

### プリミティブ型

基本の型。

**boolean型**
trueとfalse。

```js:
let hoge: boolean = false
```

**number型**

整数,浮動小数点数,正数,負数,Infinity (無限大),NaN (非数)。

```js:
let hoge: number = 1000
let fuga: number = Infinity * hoge
let huga: number = huga * NaN
```

**string型**

文字列。

```js:
let hoge: string = 'huga'
```

**symbol型**

Symbolとは、ES2015から新たに導入されたプリミティブデータ型の一つでありSymbol関数によってのみ作成できる。
競合しないユニークキーのような存在。

```js:
let sym1 = Symbol()
let sym2 = Symbol()
console.log(sym1 === sym2) // false
```

ユニークなので、オブジェクトのプロパティキーとして使用したりする。
(注意) Symbolをキーとしたオブジェクトを`for...in`でループしてもスキップされる。

```js:
// for...inループはスキップされる
const id = Symbol("id")
const obj = {
   name: "hoge",
    [id]: 123,
};
for (let key in obj) console.log(key); // name
// 直接アクセスは出来る
console.log(obj[id]) // 123 
```

`unique symbol`と明示的に型付けをすることが出来る。

```js:
const key: unique symbol = Symbol("key")  // エディタ上では typeof key と表示される
let key2: unique symbol = Symbol("key")   // let の場合はエラーが発生。変数はconstである必要がある
```

### リテラル型

ただ一つの値を表し、それ以外の値は受け付けないような型を指す。
真偽値、文字列、数値。

```js:
const sample: true = false // trueしか無理。
const sample2: 'hoge'= 'fuga' // 'hoge'しか無理。
const sample3: 123 = 321 // 123しか無理。
```

### オブジェクト型

以下のような型を使った表現方法が存在する。

```js:
const obj1: object = {}
const obj2: {} = {}
const obj3: Object = {}
```

`object`は、非プリミティブ型を表す型である。
`{}`は、いわゆる任意の値を持つプロパティが存在するオブジェクトを表す型である。
`Object`は`{}`とほぼ同じである。(厳密には違う。Objectのプロトタイプの型に割当が可能であることを強制する。)
`{}`は、プリミティブ型でもObject型に定義されたメソッドやプロパティが使えるので、エラーを検知できない。
`object`は、完全に非プリミティブ型を表しているのでエラーを検知できる。

```js:
const obj: {} = new Object(true)      // 型エラーは出ません。
const obj2: {} = true                 // 型エラーは出ません。
console.log(obj)                      // true
console.log(obj2)                     // [Boolean: true]
const obj3: object = new Object(true) // 型エラーは出ません。
console.log(obj3)                     // [Boolean: true]
const obj4: object = true             // 型エラーが出ます！！
```
上記から分かるように、JavaScriptの仕様上`const obj2: Object = true`は許される。
trueや'hoge'や123はプリミティブな値だが、暗黙的にオブジェクトが生成されるためである。(`true.toString()`みたいなことができる)
`object`型の存在経緯として、**暗黙的にObjectの型として満足できるプリミティブをJavaScriptの実行時エラーから回避するためにTypeScriptが用意したともいえる。**

### 構造的型付け

オブジェクト型に注釈を付けたい場合は、オブジェクトの形状を指定する。
例えば、`Object`型は、オブジェクトの形状を`{}`の中に明示的に注釈することができる。

```js:
const obj: { name: string } = { name: true } // 型エラー
```

TypeScriptは、あるオブジェクトが特定のプロパティを持つことを重視し、名前を気にしないスタイルを**構造的型付け**を採用している。

```js:
// オブジェクトリテラルの形状
let obj: { name: string } = { name: '' }
// クラスの形状
class Hoge {
  name string = ''
  constructor(name: string) {
    this.name = name
  }
}
obj = new Hoge('yamada') // オブジェクトリテラルとクラスインスタンスの形状は一致しているので型エラーが起きない。(構造的型付け)
```

オブジェクト型を宣言するときに使用できる修飾子として、`?`や`readonly`が存在する。

```js:
let obj: {
  a?: number
  readonly b: number
} = {
  b: 100
}
console.log(obj.a) // undefined
obj.b = 200 // 値を割り当てることができずにエラー
```
`?`は省略可能なプロパティであることを指している。(undefinedでもよい)
`readonly`は読み取り専用プロパティであることを指している。

### インデックスシグネチャ

`[key: T]: U`のような構文を指す。
あるオブジェクトが複数のキーを含む可能性があることをTypeScriptに伝える方法である。
`T`や`U`は型を指す。(`T`は`string`もしくは`number`のみである。)
`[key: T]: U`は「このオブジェクトは、型Tのキーは型Uの値を持つ」ことを意味する。

```js:
let obj: { [key: string]: boolean } = { 'isOpen': true, 'isClose': false }
```

### 配列型

ある配列の型を表現する。
宣言方法は`T[]`と`Array<T>`の2種類存在し意味も同じ。
例えば、数値の要素しか許可しない配列は下記の通りである。

```js:
const nums: number[] = [1,2,true] // 型エラー
```
上例は、数値以外の要素追加仕様としているため型エラーが起きている。
複数型を扱いたい場合配下のように宣言する。

```js:
const nums: (number|boolean)[] = [1,2,true]
```

### タプル型

タプルは配列の派生の型である。
固定長の配列を型付けするための特別な方法である。

```js:
const nums: [number] = [1] // ok
const nums2: [number] = [1, 2] // 型エラー
const nums3: [number, number?] = [1] // ok (2つ目の要素が省略可能)
```

可変長な要素もサポートしている。

```js:
const nums: [...number[]] = [1,2,3,4] // 要素何個でもOK
const nums2: [number, ...number[]] = [1,2,3,4] // 少なくとも要素が1つ必要(0個はNG)
```

### any型

何でも許す。開発者の最終手段。下記のような予期せぬミスが起きる可能性もある。

```js:
let a: any = 1
let b: any = '1'
let c: number = a + b // number型と注釈しているのにstringと推定される値を格納できてしまう
console.log(c) // "11" <- 文字列として結合されてしまっている
console.log(typeof c) // cの型を確認してみるとstringとなっている
```

### unknown型

前もって型が分からない値がある場合に使う。anyと同様に任意の値を許す。anyの変わりに使ったりする。
`typeof`等の演算子で絞り込みが可能。

```js:
let a: unknown = 'hoge'
let b: number = a // 型 'unknown' を型 'number' に割り当てることはできません
if (typeof a === 'string') { 
    let c: number = a // 絞り込みができる。( 型 'string' を型 'number' に割り当てることはできません )
    console.log(c);
}
```

### null型

null型は`null`という値の型。
`null`とは**値が存在しないこと**を意味する。

```js:
const a: null = null // OK
const b: [] | null = {} // NG: []型もしくはnullでないとエラー
```

### undefined型

undefined型は`undefined`という値の型。
`undefined`とは**まだ定義されていないこと**を意味する。 

```js:
const a: undefined = undefined // OK
const b: [] | undefined = {} // NG: []型もしくはundefinedでないとエラー
```

### void型とnever型

void型は、**明示的に何も返さない関数の戻り値の型**を意味する。
never型は、**決して戻ることのない関数の型**を意味する。

```js:
// void型を返す関数例 (何も返さない)
function func1() {
  const sum = 1 + 1 
}
// never型を返す関数例 (エラーを投げる) 
function func2() {
  throw TypeError('Error !!')
}
// never型を返す関数例 (処理が永久に続く) 
function func3() {
  while(true) console.log('Infinite Loop !!')
}
```

### 厳格なNullチェックとは

`tsconfig.json`の設定で`strictNullChecks`を無効にした場合はnullが許容されるが、有効にした場合nullが許されないこと。

```js:
// strictNullChecks: false の場合
const a: number = null   // OK
// strictNullChecks: true の場合
const a: number = null   // NG
```

`strictNullChecks`を有効にしておくことで、`NullPointerException`を事前に防ぐことができる。

```js:
function talk(user: User) { // strictNullChecksが有効であれば Null を許さない
  user.talk()
}
talk(null) // 事前に NullPointerException を阻止
```


### 型エイリアス

TypeScriptでは型エイリアスを宣言することができる。
例えば、下のような例である。

```js:
type Category = string
type Post = { title: string; category: Category; }
```

宣言した型エイリアスは下のように型として使用することが出来る。

```js:
let category: Category = 'TypeScript'
let post: Post = { title: 'TypeScript is cool!!', category: category }
```

### Union型

Union型はいわゆる**合併型**である。
Union型は`|`で表現する。
複数の型のうち一つの型が成立することを指している。

```js:
type Hoge = number | string | boolean
let hoge: Hoge = true // OK
```

### Intersection型

Intersection型はいわゆる**交差型**である。
Intersection型は`&`で表現する。
複数の型を一つに結合する。

```js:
type Yagi  = { name: string; body: boolean}
type Raion = { name: string; head: boolean}
type Hebi  = { name: string; tail: boolean}
type Kimera = Yagi & Raion & Hebi

// Kimeraは、name, body, head, tail の各プロパティが全て含まれている必要がある
const kimera:Kimera = { name: 'Kimera!!', body: true, head: true, tail: true}
```

### 列挙型

関連する値を列挙する方法。順序付けされていないデータ構造で、キーを値と対応付ける。
文字列から文字列へとマッピングするものが**文字列列挙型**である。 
文字列から数値へとマッピングするものが**数理列挙型**である。 
enumの振る舞いをより安全なサブセットに制限する**const enum型**が存在する。

```js:
// 文字列列挙型
enum Color {
  Red = '#red',
  Blue = '#blue'
}
console.log(Color.Red)    // '#red'
console.log(Color.Blue)   // '#blue'
console.log(Color.Green)  // 型エラー:  'Green' は存在しない

// 数理列挙型
enum Color {
  Red,
  Blue
}
console.log(Color.Red)        // 0         <- 値でアクセスできる
console.log(Color[1])         // 'Blue'    <- キーでアクセスできる
console.log(Color[2])         // undefined <- 型エラーが起きない!!
console.log(Color[Color.Red]) // 'Red'     <- 値アクセスして取り出したキーでアクセスできる

// const enum型
const enum Color {
  Red,
  Blue
}
console.log(Color.Red)        // 0
console.log(Color[1])         // 型エラー: 文字列リテラルを使用してのみアクセスできる
console.log(Color[2])         // 型エラー: 文字列リテラルを使用してのみアクセスできる
console.log(Color[Color.Red]) // 型エラー: 文字列リテラルを使用してのみアクセスできる
```

以上になります。