FluxのactionTypeはなぜ唯一である必要があるのか

## 前提知識
fluxをある程度

## 経緯
ある日[typescript-fsa](https://github.com/aikoven/typescript-fsa)の話をしました。

```typescript
import actionCreatorFactory from 'typescript-fsa';

const actionCreator = actionCreatorFactory();

const a = actionCreator('A');
const b = actionCreator('A');
```

たとえばreducerが2つあって、一番ベーシックな書き方の場合は、`a.type`と`b.type`とかでswitch文を書くかと思います。
今回の場合、aのtypeは`A`で、bのtypeは`B`です。
このa又はbのactionを送出した場合、2つのreducerは両方反応してしまうのはわかりきっていることだと思います。
しかし、とある方から反応しないと思うよ！と意見を頂きました。
一応`typescript-fsa`の実装コードも読んでたのでそんなはずはない！と思ったんですよね。
でも、もしかしたら凄い魔法がかかってる可能性も無きにしもあらずですし、憶測だけで話すのもよくないので、ちゃんと検証しようと思い、それっぽいコード書いてconsoleでログを出しながら実行して検証してみました。
https://github.com/k-okina/research-redux-typescript-fsa

結果、やはり両方反応しました。

しかし、それってイケてないよね。ライブラリの`actionCreator`から生成される`actionCreator`は、全て違うものとして識別して欲しいよねって意見を頂いたので、果たして本当にその必要があるのか？🤔と考察する機会を得られたのでその考察内容を共有したいと思います。

## typescript-fsaの仕事
`typescript-fsa`はただ、`flux standard action`、略して`fsa`の仕様に沿ったactionCreatorが作れるよ！ってのが責務です。
fsaの仕様とかにはactionCreatorから作られるactionのactionTypeは唯一でなければならないとは書かれていません。なのでtypescript-fsaにactionTypeの管理も任せたいってのはお門違いな話だと思います。
また、唯一にしたいのって、その人個人、又は特定条件下のプロジェクトだけの話の可能性もあります。
皆本当にそんな事して欲しい？🤔本当に他の反応しないでほしいの？🤔

## 検証
実際に偶然actionTypeが一致してしまった場合のサンプルを用意しました。
以下がそれです。

```typescript:actions.ts
import actionCreatorFactory from 'typescript-fsa'

const actionCreator = actionCreatorFactory()

const actionType = 'THIS_IS_ACTION_TYPE'
export const a = actionCreator<{foo: string}>(actionType)
export const b = actionCreator<{bar: string}>(actionType)
```

```typescript:reducerA.ts
import {Action} from 'redux'
import {isType} from 'typescript-fsa'
import {a} from './actions'

type State = {foo: string}

export const reducerA = (state: State, action: Action): State => {
  if (isType(action, a)) {
    // action.payload is inferred as {foo: string}
    return {foo: action.payload.foo}
  }

  return state
}
```

```typescript:reducerB.ts
import {Action} from 'redux'
import {isType} from 'typescript-fsa'
import {b} from './actions'

type State = {bar: string}

export const reducerB = (state: State, action: Action): State => {
  if (isType(action, b)) {
    // action.payload is inferred as {bar: string}
    return {bar: action.payload.bar}
  }

  return state
}
```

```typescript:store.ts
import reducerA from './reducerA'
import reducerB from './reducerB'
import {createStore, combineReducers} from 'redux'

export default createStore(combineReducers({reducerA, reducerB}))
```

例えばこの2つのサンプルreducerをcombineして作られたstoreに対して以下のようなactionを投げてみます。

```typescript:index.ts
import store from './store'
import {a} from './action'
store.dispatch(a({ foo: 'hello world' }))
```

`actions.ts`ファイルを見ると分かる通り、2つのactionは全く同じaction typeです。
ですので、`reducerA`と`reducerB`両方が反応しますが、`reducerB`の方は`payload.bar`を期待しているのに、undefined参照をしてしまいます。
以下がその解説です。

```typescript:reducerB.ts
import {Action} from 'redux'
import {isType} from 'typescript-fsa'
import {b} from './actions'

type State = {bar: string}

export const reducerB = (state: State, action: Action): State => {
  if (isType(action, b)) {
    // action.payload is inferred as {bar: string}
    // But received action is not a b action!!
    // So referenced to undefined variable!!! 😰
    return {bar: action.payload.bar}
  }

  return state
}
```

これは本来意図しない結果です。
もし同一actionTypeを使いたいなら、UnionTypeをしないといけないのが現実です。

```typescript
export const correctAction = actionCreator<{bar: string} | {foo: string}>(actionType)
```

## まとめ
つまり、同一`actionType`を使って複数の`action`を作る事は一切無さそうです。
せっかく`typescript`で型付けしても簡単に壊される可能性があります。
なので、`actionCreator`レイヤーで守るか、`actionTypeGenerator`を用意し、その中で守る必要がありそうです。
`typescript-fsa`では`actionCreatorFactory`で`prefix`を渡して`actionType`を調整したりすることもあるので、多分一番現実的な解決方法は、`actionCreator`内で`actionType`に`id`採番をするとかでしょうか。