Flowtypeにおける$ExactとObject Rest Spread

## 前提

(※ 以下の話は現行バージョンのFlow(flowtype) = version 0.49 での話)

Flow (flowtype) において、他にプロパティをもたないオブジェクトを定義するのに、[`$Exact`](https://flow.org/en/docs/types/utilities/#toc-exact)とよばれるUtility Typeが存在する。

```js
type A = { foo: number, bar: string };
const a1: A = { foo: 1 }; // NG
const a2: A = { foo: 1, bar: 'text' }; // OK
const a3: A = { foo: 1, bar: 'text', baz: true }; // OK

type B = $Exact<A>;
const b1: B = { foo: 1 }; // NG
const b2: B = { foo: 1, bar: 'text' }; // OK
const b3: B = { foo: 1, bar: 'text', baz: true }; // NG
```

なお、`＄Exact`のかわりに `{| foo: number, bar: string |}` のような指定もできる。

## Object Rest/Spreadにおける問題

ここで、Reduxのreducerをコーディングするときなど、よく[Object Rest/Spread](https://github.com/tc39/proposal-object-rest-spread)という仕組みを使って書くことが多い。Object rest/spread自体は現在stage 3であるが、babelを利用する環境ではpluginで組み込むことが多い。

しかし、Exact typeを使っていると、ここで以下のようなエラーが出てしまう

```js
type B = $Exact<{ foo: number, bar: string }>;
const b1: B = { foo: 1, bar: 'text1' };
const b2: B = { ...b1, bar: 'text2' }; // NG
/* 
=>
  const b2: B = { ...b1, bar: 'text2' };
                 ^ object literal. Inexact type is incompatible with exact type
*/
```

Exact object typeが使えないと、たとえばReduxにおけるreducerのコードは、たとえば以下のようなtypoを見逃してしまう可能性が高い。

```js
type State = { foo: number, bar: string };　// not exact type

const initState: State = { foo: 1, bar: '' };

// this typo can not be checked by flowtype
function reducer(state: State = initState, action: Action) {
  if (action.type === 'MODIFY_BAR') {
    return { ...state, barr: '<= prop name typo is here!!!' };
  }
  return state;
}
```

## 回避策とその限界

これについてはすでに[issueにて議論があり](https://github.com/facebook/flow/issues/2405)、以下のような回避方法を提案している人もいる。

```js
type Exact<T> = T & $Shape<T>;

type B = Exact<{ foo: number, bar: string }>;
const b1: B = { foo: 1, bar: 'text1' };
const b2: B = { ...b1, bar: 'text2' }; // OK
const b3: B = { foo: 1, bar: 'text3', baz: false }; // NG
```

なんとなく回避策でもよさそうな気がするが、もうすこし複雑なType、たとえばUnionを使った場合は、どうなるか。

```js
// using normal exact type notation
type C1 = $Exact<{
  version: '1',
  foo: number,
  bar: string,
}> | $Exact<{
  version: '2',
  foo: number,
  bar: string,
  baz: boolean,
}>;

const c11: C1 = { version: '1', foo: 1, bar: 'text' }; // OK
const c12: C1 = { version: '1', foo: 1, bar: 'text', baz: true }; // NG
const c13: C1 = { version: '2', foo: 1, bar: 'text', baz: true }; // OK
const c14: C2 = { version: '2', foo: 1, bar: 'text', baz: true, qux: 'hello' }; // NG


// using custom exact
type Exact<T> = T & $Shape<T>;

type C2 = Exact<{
  version: '1',
  foo: number,
  bar: string,
}> | Exact<{
  version: '2',
  foo: number,
  bar: string,
  baz: boolean,
}>;

const c21: C2 = { version: '1', foo: 1, bar: 'text' }; // OK
const c22: C2 = { version: '1', foo: 1, bar: 'text', baz: true }; // NG
const c23: C2 = { version: '2', foo: 1, bar: 'text', baz: true }; // OK
const c24: C2 = { version: '2', foo: 1, bar: 'text', baz: true, qux: 'hello' }; // NG
```

ここまで、上記のコードではC1(標準のExact Typeを利用)とC2(回避策であるカスタムのExact Typeを利用)とで同じ結果になっているが、以下のコードでつまづく。

```js
// OK
function baz1(c1: C1): boolean {
  if (c1.version === '2') {
    return c1.baz;
  }
  return false;
}

// NG
function baz2(c2: C2): boolean {
  if (c2.version === '2') {
    return c2.baz;
  }
  return false;
}
/*
=>
    return c2.baz;
               ^ property `baz`. Property cannot be accessed on any member of intersection type
*/
```

　つまり、上記の回避方法のカスタムExactでは、Union typeでのプロパティアクセス、特にtype refinementが絡まってくるケースには使えないようである。

## 上記問題の回避策

見つかっていない。union type の利用が不可避なstate設計では、Exact typeを諦めるのが正しいのだろうか？