reducerの合成をFlowは型推論してくれるのか?

# あらまし

---

最近Redux勉強した。

---

## Reduxとは何か?

---

イベントソーシングを行うためのライブラリ (個人の感想)

---

**イベントを再生してステートを構築する**

注: Reduxにおいてはイベントはアクションと呼ばれています

---

## reducer

アクションからステートをつくる役割を持つ

型的には `(state: S, action: {type: string}) => S` な関数

現在の状態とアクションを受け取って新しい状態を構築する

---

## Reduxが持つAPI

---

* createStore
* **combineReducers**
* bindActionCreators
* applyMiddleware
* compose

---

# combineReducersでreducerをくっつけよう

---


## 例

```javascript
const arr = (state: number[]  = [1], action: Action) => state;
const num = (state: number    = 1  , action: Action) => state;

const action = {type: 'hoge'};

const reducer = combineReducers({arr, num});
```

`number[]`を返すreducerと `number`を返すreducerをくっつけた。
つくったreducerは `{arr: number[], num: number}` という型の値を返す

---

## つかってみる

```
const state = reducer({arr:[1], num: 1}, action); // 型OK
```

---

## 渡すstateがまちがってる場合

型エラーになる。 num がないといわれて素晴らしい

```
reducer({arr:[1]}, action);
```

---

## 戻り値の型の確認

```
const ns: number[] = state.arr; // 型ok
// const ns2: number[] = state.num; // 型エラー numの型はnumber
// const n: number = state.hoge; // 型エラー 存在しないプロパティを使おうとしている
```

---

## 引数につかった値の確認

合成したreducerには間違った型の値を渡せるが、戻り値を使う時は正しい型で使えている

```
const state2 = reducer({arr: 'hoge', num: 'hogehoge'}, action); // okarrの値もnumの値も型が認識できてない
const s: number = state2.num; // 型ok reducerの戻り値は型推論できてる
const s2: string = state2.arr; // 型エラー 渡す値は間違ってるけど、戻り値を使う時は正しい
```

## 戻り値を代入する型を指定してみる

引数の型の値は認識できてないが、戻り値はばっちり。

```
const state3: {num: string} = reducer({arr: 1, num: 'hogehoge'}, action); // 型エラー 使おうとすると numの型をちゃんと認識してくれる
```

## TypeScriptもためしてみた

```
const reducer = combineReducers<{arr: number[]}>({arr, num}); // 型ok 引数から型推論してくれない。型パラメータを正しく指定する必要があった。嘘もつける。
```

---

```
const state = reducer({arr: [1]}, action); // ok numがないけどとおってしまう
```

---

引数の型はちゃんと認識する。


```
reducer({arr: 1}, action); // 型エラー 指定してる型と違う
```

----

型パラメータを指定してないと、

```
const reducer = combineReducers({arr, num});
const state = reducer({arr: [1]}, action);
const ns: number[] = state.arr; // 型エラー combineReducersで型パラメータを指定してないので型エラーになる
```


---

# まとめ

---

Q. reducerの合成をFlowは型推論してくれるのか?

A. してくれた。

---

* Flow
  * ちゃんとreducerの合成を認識してくれた
  * しかし、reducerのつくったstateを使わない場合、型が合わない値が渡せた
* TypeScript
  * うまく認識できなかったので、型パラメータを指定した
  * 型パラメータを指定さえすれば良い感じ
  * 引数のkeyの種類とか嘘をつけるけど、使う場合は必要になるので問題ない
* どっちも一長一短だった

---

# 参考コード

## Flow

```javascript
// @flow
import { combineReducers } from 'redux'

type Action = {type: string}; // TypeScriptでActionが必須だったので合わせる

const arr = (state: number[]  = [1], action: Action) => state;
const num = (state: number    = 1  , action: Action) => state;

const action = {type: 'hoge'};

const reducer = combineReducers({arr, num});

// reducer({arr:[1]}, action); // 型エラー num がないといわれる。素晴らしい

const state = reducer({arr:[1], num: 1}, action);

const ns: number[] = state.arr; // ok
// const ns2: number[] = state.num; // 型エラー ちゃんと認識している
// const n: number = state.hoge; // 型エラー 存在しないプロパティを使おうとしている

const state2 = reducer({arr: 'hoge', num: 'hogehoge'}, action); // okarrの値もnumの値も型が認識できてない
const s: number = state2.num; // ok reducerの戻り値は型推論できてる
const s2: string = state2.arr; // 型エラー 渡す値は間違ってるけど、戻り値を使う時は正しい

// const state3: {num: string} = reducer({arr: 1, num: 'hogehoge'}, action); // 型エラー 使おうとすると numの型をちゃんと認識してくれる
```

## Typescript

```javascript
import { combineReducers, Action, Reducer } from 'redux'

const arr = (state: number[]  = [1], action: Action) => state;
const num = (state: number    = 1  , action: Action) => state;

const action = {type: 'hoge'};

const reducer = combineReducers<{arr: number[]}>({arr, num}); // ok 引数から型推論してくれないので戻り値の型を正しく指定しないといけない
const state = reducer({arr: [1]}, action); // ok numがないけどとおってしまう
// reducer({arr: 1}, action); // 型エラー 指定してる型と違う

// const ns: number[] = state.arr; // 型エラー combineReducersで型パラメータを指定しておかないと型エラーになる

```

## 参考

* combineREducersの型定義(Flow) https://github.com/reactjs/redux/blob/master/flow-typed/redux.js#L51
* combineReducersの型定義(TypeScript) https://github.com/reactjs/redux/blob/master/index.d.ts#L73