TypeScriptの関数について TypeScript & Angular 勉強会 #3

## 前回の演習問題
以下のコードを、`enum`ではなくUnionを使うように変更してください。

```ts
enum Tiles {
  None = 0,
  AllSimples = 1,
  AllRuns = 1 << 1,
  Reach = 1 << 2,
  SelfDraw = 1 << 3,
  FirstTurnWin = 1 << 4
}

function printTile(tiles: Tiles):string {
  let result = ""

  if(tiles & Tiles.Reach){
    result += "リーチ"
  }

  if(tiles & Tiles.FirstTurnWin){
    result += "一発"
  }

  if(tiles & Tiles.SelfDraw){
    result += "ツモ"
  }

  if(tiles & Tiles.AllSimples){
    result += "断么九"
  }

  if(tiles & Tiles.AllRuns){
    result += "平和"
  }
  

  return result
}

let tile = Tiles.Reach | Tiles.SelfDraw

console.log(printTile(tile)) //リーチツモ
```

## 解説
これが所謂「ビットフラグ」です。

```ts
enum Tiles {
  None = 0,
  AllSimples = 1,
  AllRuns = 1 << 1,
  Reach = 1 << 2,
  SelfDraw = 1 << 3,
  FirstTurnWin = 1 << 4
}
```

このように論理和演算子を使うことで、複数の条件を格納することができます。

```ts

let tile = Tiles.Reach | Tiles.SelfDraw
```

ただしチェックはあんまり直感的ではありません。

```ts                      
if(tiles & Tiles.FirstTurnWin){
    result += "一発"
}
```

---

## 回答例
ビットフラグをUnionで実現しようとすると、配列を使うのが手っ取り早いですね。

```ts
type Tile = "AllSimples" | "AllRuns" | "Reach" | "SelfDraw" | "FirstTurnWin"

function printTile(tiles: Tile[]):string {
  return tiles.reduce((acc, cur) => {
    switch(cur){
      case "AllSimples":
        return acc += "断么九"
      case "AllRuns":
        return acc += "平和"
      case "Reach":
        return acc += "リーチ"
      case "SelfDraw":
        return "ツモ"
      case "FirstTurnWin":
        return "一発"
    }
  }, "")
}

let tiles:Tile[] = ["Reach", "AllRuns"]

console.log(printTile(tiles)) //リーチ平和
```

※ reduce などの集約関数は別途まとめて会を作ります

---

## 関数定義

```ts

function greet(name: string): void {
  console.log(`hello, ${name}`)
}
```

## いろいろな関数定義

Typescriptでは関数は第一級のオブジェクトであり、数字や文字列などと変わりません。
下記のように変数として定義できます。

```ts
// 関数式
let greet2 = function(name: string): void {
  console.log(`hello, ${name}`)
}

// アロー関数
let greet3 = (name: string) => {
  console.log(`hello, ${name}`)
}

// アロー関数の省略記法
let greet4 = (name: string) => console.log(`hello, ${name}`)
```

関数式とアロー関数は実装上は同じ、、、ではない。

- [JavaScript: 通常の関数とアロー関数の違いは「書き方だけ」ではない。異なる性質が10個ほどある。](https://qiita.com/suin/items/a44825d253d023e31e4d)

ただし、Typescriptでクラスベースで使う文にはそこまで大きな影響ないので、そこまで詳しい解説は省略します。

---
## オプションパラメータ、デフォルトパラメータ
- 省略可能なオプションパラメータ、デフォルト値があるデフォルトパラメータ
  - 他の言語でもおなじみですよね（オーバーロードで実現する例とかもありますが）


### オプションパラメータ

```ts
type Language = "jp" | "en"

function helloWorld(name: string, lang?: Language){
  if(lang == null){
    lang = "jp"
  }

  switch(lang) {
    case "en":
      console.log(`Hello, ${name}`)
    case "jp":
      console.log(`こんにちわ、${name}`)
  }
}

helloWorld("世界")
```

### デフォルトパラメータ

```ts

type Language = "jp" | "en"

function helloWorld(name: string, lang: Language = "jp"){
  switch(lang) {
    case "en":
      console.log(`Hello, ${name}`)
    case "jp":
      console.log(`こんにちわ、${name}`)
  }
}

helloWorld("世界")
```

個人的にはデフォルトパラメータをおすすめします（`null` を考慮するのは思考コストが高い）

---
### レストパラメータ
- 任意の数のパラメータを配列として受け取る機能。いわゆる**可変長引数**
- 「レスト」の名前のとおり、最後の引数としてのみ指定可能

```ts
function sum(...value: number[]): number {
  return value.reduce((acc, val) => {
    return acc + val
  }, 0)
}

console.log(
  sum(1,2,3) //好きな長さのパラメータを渡せる
  )

function accumulate(accFunc: (acc: number, cur: number) => number, ...value: number[]): number {
  return value.reduce((acc, val) => {
    return accFunc(acc, val)
  }, 0)
}

console.log(
  accumulate((acc, cur) => acc + cur , 1,2,3) //好きな長さのパラメータを渡せる
  )
```

---
## `call`, `apply`, `bind`
- すべてJavascriptが持つ機能で、関数の呼び出しを細かく制御したいときに使う
    - 呼び出しコンテキスト（`this`）の制御
    - `bind` のみ、コンテキストを指定した別関数を返す

```ts
const add = (left: number, right: number) => left + right

console.log( add(10, 20) )
console.log( add.apply(null, [10, 20]) )
console.log( add.call(null, 10, 20) )
console.log( add.bind(null, 10, 20)() )
```

- マニアックなライブラリでも使おうと思わない限り、めったに使わないのでそんなに覚えなくていいです
- ~~JS知識マウント取りたい場合に使ってください~~

---
## 関数の `this`
Javascript / Typescript では関数の呼び出しコンテキストによって `this` が異なる

```ts
let x = { 
    a() {
        return this
    }
}
console.log( x.a() )  //{}

let a = x.a

console.log( a() ) // undefined
```

この呼出コンテキストを `this` を片付けすることにより制御することができる。

```ts
let x = { 
    arg1: 1,
    a() {
        return this
    },
    b(this: {arg1: number}) {
        return this
    }
}
console.log( x.a() )  //{}

let a = x.a
let b = x.b

console.log( a() ) // undefined

b() //The 'this' context of type 'void' is not assignable to method's 'this' of type '{ arg1: number; }'.(2684)
```

こいつとclassを組み合わせることで非常に面白いことができます。お楽しみに！！

---
## 関数の型付け
ここまで暗黙的にやって来ましたが、関数の型付け記法があります。

```ts
type funcA = (arg1: number, arg2: string) => void
```

関数自体をプロパティに持つような定義も可能

```ts
type ObjWithFunc = {
    funcA: () => void
}
```

 ---
## 関数の型付け（完全版）
以下の型付けは省略記法になります。

```ts
type Log = (message: string, userId: string) => void
```

以下が正式な記法です。

```ts
type Log = {
  (message: string, userId: string): void
}
```

普通は省略記法を使ってください。しかし、正式記法ではないと表現できないこともあります。
それが関数のオーバーロード

---
## 関数のオーバーロードとは

- 説明せよ（唐突に演習）

---
## 関数のオーバーロード定義
正式な関数の定義記法でしか定義できません。

```ts
type Log = {
  (message: string): void
  (message: string, userId: string): void
  (module: number, message: string, userId: string): void
}
```

これの実装はどうすればいいのでしょうか？

---
## 関数のオーバーロード実装
オーバーロードの実装には、**すべてのパラメータパターンを受けることができる一つの関数を実装しなかればならない。**


```ts
let log: Log = (
    messageOrModule: string | number, 
    messageOrUserId?: string, 
    userId?: string) => {
    //TODO
}
```

---
## ジェネレータとイテレータ
ジェネレータとイテレータは双方Javascript自体の機能なので、そこまで深堀りしません。
ただこういう機能があることを知っておくと良いです。特に漸化式で表すことができるような値との相性は良いです（フィボナッチ数列など）

### ジェネレータ
ジェネレータは、値を一つずつ返すオブジェクトのこと。`function*` で定義できる。

```ts
function* enumulateNumbers() {
  let n = 0
  while(true){
    yield n++
  }
}

let numbers = enumulateNumbers()

console.log( numbers.next().value ) //0
console.log( numbers.next().value ) //1
console.log( numbers.next().value ) //2
```

### イテレータ
イテレータは反復可能なオブジェクトのこと。`for ... of ...` の構文などで中身を展開できる。

```ts
let enumulater = {
  *[Symbol.iterator]() {
    for (let n = 1; n <= 10; n++) {
      yield n 
    }
  }
}

for(let item of enumulater){
  console.log(item)
}
```