TypeScript の基本構文

# 概要
知人、社内向けに書きます。
型ってなんだよレベルは脱出してる前提です。

# 参考
- [公式サイト - Typed JavaScript at Any Scale.](https://www.typescriptlang.org/)
- [公式リファレンス - TypeScript Documentation](https://www.typescriptlang.org/docs/)
- [TypeScript Playground](https://www.typescriptlang.org/play)
  - Javascriptコードへのコンパイル結果の確認や型チェックを行いながらTypeScriptのコードを書けるツール

# 型について
###明示的な型付け
TypeScriptでは明示的に型付けを行うことが可能。

```tsx
let umaru : number;
umaru = 108;
umaru = '108'; //エラー
```
変数名に続けてコロン:と型をを書くことで変数の型を指定することが可能。
上記コードは変数**umaru**は**number**型と明示している。
**number**型である108は代入可能だが、**string**型である'108'は代入不可。

```tsx
function double(n: number): number {
  return 2 * n;
}

function errorFunc(n: number): number {
  return n.toString(); //エラー
}
```
関数の引数は変数の型付けと同様の方法で型を指定することが可能。
関数の戻り値の型を明示する場合は、引数を取り囲むカッコの直後にコロン:と型を書けばOK。

###暗黙的な型付け
コードから型を推測し、変数や関数に型を付与してくれる。あざす。

```tsx
let umaru = 108;
umaru = '108'; //エラー
```
この例だと変数**umaru**が108という数値、即ち**number**型で初期化されている。
この時点で**umaru**は**number**型に確定となり、続く**string**型である'108'の代入は、型が異なることによるエラーとなる。

```tsx
function double(n: number) {
  return n * 2;
}

let umaru = double(1);
//→ umaru = 2
umaru = 'taihei'; //エラー
```
関数の戻り値の型を指定しないパターン。
この場合、関数**double**の戻り値は関数内のコードから**number**型と推測することが可能。
変数**umaru**についても**number**型と確定され、**number**型以外の値の代入はエラーとなる。

このようにTypeScriptは型を推測しながらエラーがないかチェックしてくださる。
すべての型を我々開発者側で明示する必要は無い。

？？？「わぁいTypeScript あかりTypeScript大好き」

### 基礎的な型 
標準で使える型は以下の通り。

| 型名         | 内容                | 
|:-------------|--------------------|
| string       | 文字列             |
| number       | 数値               |
| boolean      | 真偽値             |
| any          | 型チェックを行わない|
| Array        | 配列               |
| Tuple        | 組み合わせ         |
| Object       | オブジェクト       |
| null         | null              |
| undefined    | undefined         |
| void         | 他のどの型でもない |

- **string**
ダブルクォーテーション「"」やシングルクォーテーション「'」で括った文字列は**string**型となる。

- **number**
JavaScriptで扱える数値を表す。
10進数だけじゃなくて、2,8,16進数も扱える。

- **boolean**
**true**もしくは**false**のどちらかを取る。

- **any**
anyは型チェックを必要としないという意思表示となる。

```tsx
function double(n: any) {
  return 2 * n
}

var umaru_chan = double('42'); //①エラーとならない
```

①で関数doubleを引数'42'を指定して呼び出しているが、エラーにはならない。
・・・が実行時にもエラーにはならずNaNが帰ってくる。
anyはあまり使わないようにしよう。

- **ArrayとTuple**<br>
**Array**は配列のことで、長さが特に決まっていないものを指す。<br>
また、配列の中身は1つの型しか指定出来ない。<br>
**Array型**に続けて中身の型を<と>で囲んで指定するか(①)、中身の型に続けて[ ]を書くことで(②)配列の型を表すことが出来る。

```tsx
var arrayUmaru1: Array<number> = [1,2,3]; //①
var arrayUmaru2: number[] = [1,2,3];      //②
```
それに対し**Tuple**は長さを明示する必要があるが、中身に異なる型を指定することが出来る。

```tsx
var tuple: [number, string] = [1, 'foo'];
```

- **Object**<br>
オブジェクトも型として表すことが出来る。

```tsx
function area(rect: {length: number, width: number}) {
  return rect.length * rect.width;
}

var umaru1 = area({length: 3, width: 4});
var umaru2 = area({length:3});                           //エラー -= ∧＿∧
var umaru3 = area({width: 4});                           //エラー =と(`･ω･´)
var umaru4 = area({length: 3, width: 4,extra: 'hoge'});  //エラー -=/　と_ノ
```
関数areaの引数の型として**{length: number, width: number}**を指定している。
つまり、**number**型である**length**プロパティと、**number**型である**width**プロパティのみを持つオブジェクトを受け付けるという意味。
コードの最後の3行のように、必要なプロパティが足りなかったり、型定義に存在しないプロパティが存在している場合はエラーとなる。

- **nullとundefined**<br>
**null**型と**undefined**型は、それぞれJavaScriptにおける**null**と**undefined**に対応。
TypeScript標準の動作では、これら2つの型はすべての方に対して代入可能。

```tsx
var umaru1: number = null;      //エラーにならない
var umaru2: number = undefined; //エラーにならない
```
・・・がこのままだと各変数が**null**か**undefined**ではないことを確かめてからでないと使えない。
面倒なので、オプションである**strictNullChecks**をオンにすることを推奨。
オンにすることで、それぞれの型もしくは**any**のみが代入可能となる。↑のコードはエラーとなる。
設定方法は割愛。

- **void**<br>
**void**は他のどの型でも無いことを示す型。

```tsx
function log(umaru: any){
  console.log(umaru);
}
```
この関数の返り値は無し。コンパイルして型宣言を出力させると以下の通りとなる。

```tsx
declare function log(umaru: any): void;
```
返り値の型として**void**が付与されていることが確認できる。

### 新たな型を作成
前述の基礎的な型を使って新しい型を定義することができる。

- **interface**<br>
 
```tsx
interface UmaruChan {
  length: number;
  width: number;
}

function area(umaruChan: UmaruChan){
  return umaruChan.length * umaruChan.width;
}

var umaru1 = area({length: 3, width: 4});
var umaru2 = area({length:3});                           //エラー ∧,,∧ 
var umaru3 = area({width: 4});                           //エラー ( 'ω' )つ 
var umaru4 = area({length: 3, width: 4,extra: 'hoge'});  //エラー （m9 ＼ 
```
**number**型である**length**プロパティと、**number**型である**width**プロパティを持つ、新しい型***UmaruChan***を定義。
また、定義したプロパティは過不足持ってなければならない。エラーとなる。
これだと使いづらいことがあるので、指定が任意であるプロパティを定義することができる。

```tsx
interface NetworkOptions {
  timeout?: number;
  numofRetry?: number;
}

var options01: NetworkOptions = {
};

var options02: NetworkOptions = {
  timeout: 3000
};

var options03: NetworkOptions = {
  numofRetry: 3,
};

var options04: NetworkOptions = {
  timeout: 3000,
  numofRetry: 3,
};

var option05: NetworkOptions = {
  timeout: 3000,
  numofRetry: 3,
  umaruChan: 1,  //エラー！！！！！！！！！！！！！！！！！！！！！！！
}
```
例としてネットワーク通信の設定用オブジェクトを定義。
**timeout**プロパティは通信がタイムアウトするまでの時間を、
**numofRetry**はリトライ回数を指定するためのプロパティという想定で定義。
これらが指定されていない場合、それぞれのデフォルト値を用いてネットワーク通信を行うものとする。

**timeout**プロパティおよび**numofRetry**プロパティそれぞれの末尾に「**?**」が書かれているが、
これを書くことで指定が任意であることを指示することができる。
**NetworkOptions**型を使用する際、**timeout**プロパティと**numofRetry**プロパティについては任意の指定が可能となるが、
型定義に存在しないプロパティの指定はエラーになる。

- **Type Aliasing**<br>

```tsx
type Id = number
```
以上のように、型に別名をつけることも可能。ここでは**Id**型を**number**型の別名として定義している。

- **リテラル型**<br>
ある特定の数値や文字列も型として利用することが可能。

```tsx
type One = 1;
type Umaru = 'umaru';

var one: One = 2;             //エラー
var umaru: Umaru = 'taihei';  //エラー
```
**One**型は**1**型の別名、**Umaru**型は**'umaru'**型の別名として定義。それぞれ**1**と**'umaru'**以外の値は代入不可。
これを**リテラル型**と呼ぶ。次の**Union Types**と併用すると便利になる型。

- **Union Types**<br>
**Union Types**は和集合を定義することが可能。

```tsx
type Action = {
  type: 'increment';
} | {
  type: 'add';
  payload: number;
};

function increaser(state: number, action: Action) {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'add':
      return state + action.payload;
  }
}
```
ここでは、操作を表す**Action**を、**type**プロパティが**'increment'**型であるものと**'add'**型であるもののいずれかをとる型として定義。片方にのみ**payload**プロパティを定義してみた。
関数**increaser**の内部では、**switch**によって処理を分岐させている。
このとき、TypeScriptはUnion Typesを構成する型のうち、その文脈における型を判別してくれる:point_up_2:

例を上げると、**case 'increment'**節では**payload**プロパティが存在しないことを検知してくれるので、**action.payload**が存在しないことをエラーで出力してくれる。

```tsx
const action : Action = {
    type: 'add',
};

console.log(increaser(1,action));

//出力エラー↓
Type '{ type: "add"; }' is not assignable to type 'Action'. 
Property 'payload' is missing in type '{ type: "add"; }' but required in type '{ type: "add"; payload: number; }'.
```

- **Genetics**<br>
**ジェネリック**と**ジェネリクス**のどっちが一般的なんでしょうね。

```tsx
function double(n: number) {
  return 2 * n;
}
function concat(str: string) {
  return str + str;
}

// 関数 doubleCall を定義して以下のように使いたい
doubleCall(2,double);
doubleCall('abc',concat);
```
関数**double**は**number**型を受け取り**number**型を返し、関数**concat**は**string**型を受け取り**string**型を返す。
これらの関数を2回呼ぶ関数**doubleCall**を定義したい、こういった場合に**Generics**の登場:muscle:

```tsx
interface SameTypeFunction<T> {
   (src: T): T;
}

function doubleCall<T>(src: T, func: SameTypeFunction<T>) {
  return func(func(src));
}

function double(n: number) {
  return 2 * n;
}
function concat(str: string) {
  return str + str;
}

function toStrUmaru(n : number) {
  return 'UMARU';
}

doubleCall<number>(2,double);
// 型推論によって明示的なパラメータ指定が不要になっている。
doubleCall(2,double);

doubleCall('abc',concat);
doubleCall(2,toStrUmaru); //エラー
```
関数名の直後において **<>** で型パラメータ名を括って型指定をすべき箇所に型パラメータ名を指定する。
**SameTypeFunction**型はなんらかの型**T**を受け取り**T**を返す関数を定義している。
**doubleCall**関数の第2引数の型として**SameTypeFunction**型をしているが、この型パラメータもGenericsによって指定可能としている。

Genericsを用いて型を使用する際は通常、 **<>** にパラメータを指定する必要がある。
ただし、**doubleCall**関数では第1引数によって**T**型によって推論可能であるため、型パラメータの指定無しで呼び出すことも可能。

最後の例のように**number**型をとり**string型を返す**toStrUmaru**関数を用いた**doubleCall**関数の呼び出しはエラーとなる。
**SameTypeFunction**による制約に違反するため。~~制約と誓約~~

- **Optional Chaining**<br>
[TypeScript3.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining)から利用可能となった

```tsx
interface Options {
  actions?: {
    callback: () => void;
  }
}

function area(props: Options){
  if (props.actions) {
    props.actions.callback()
  }
}
```
**Options**型では、プロパティ**actions**型が存在する場合と存在しない場合があるが、上のコードのように**actions**プロパティが存在するか確かめる必要がある。

**Optional Chaining**は、明示的なチェックを省いてプロパティの値を参照可能とするもの。下記のように書ける。

```tsx
//「?.」の左側に指定されているプロパティが定義されていなければ何もしない
function area(props: Options) {
  props.actions?.callback()
}
```