Reactにflowtypeを導入したまとめ

Reactで作っているwebアプリにflowを導入したので雑にまとめる。
書き方が間違っている部分は指摘していただけると嬉しい。

[2017.12.13] Flowのバージョンアップによって書き方変わった部分があるので修正しました。URLも変更しています。

# flowについて
- Facebook製のJavaScript静的型チェッカー
- 言語ではなく外部ライブラリ
- nullやundefinedにとても厳しい

### 参考サイト
**公式**
https://flow.org/en/docs/
**チートシート**
https://www.saltycrane.com/blog/2016/06/flow-type-cheat-sheet/

# 設定など

## flowインストール
基本はこのページ参照。
https://flow.org/en/docs/install/

## .flowconfig
読み込むファイルの拡張子はここで定義する。
optionsの先頭では、 `import` で絶対パスが使用できるように設定している。

```ini:.flowconfig
[ignore]
.*/node_modules/.*

[include]

[libs]
flow-typed

[options]
module.name_mapper='^\(.*\)$' -> '<PROJECT_ROOT>/src/\1'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.css
module.file_ext=.scss
module.file_ext=.json
```

## flowチェック
package.jsonにflowのscriptを追加して、ローカルのflowを使えるようにする。

```package.json
  "scripts": {
    "flow": "flow",
  },
```

```
yarn flow
```

## flow-typedインストール
外部ライブラリの型定義をしてくれるライブラリ。
https://github.com/flowtype/flow-typed

```
yarn global add flow-typed
```

skipオプションを付けないと、大量のstub（ゴミファイル）が生成されてしまう。
参考：https://github.com/flowtype/flow-typed/wiki/CLI-Commands-&-Flags#flags-1

```
flow-typed install --skip
```

ライブラリにflow-typedが対応しているか調べるには下記。

```
flow-typed search xxx
```

# 基本的な書き方
## 全体
flowで型チェックをしたいファイルの上部に下記のようなコメントアウトを入れる。

```js
// @flow
```

## Functional Component
Functional Componentの場合、下記のような書き方で `props` と `型` と `defaultProps（必要なら）` を定義する。
https://flow.org/en/docs/react/components/#toc-stateless-functional-components
この書き方はES2015の[分割代入](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)を使っている。

```js
// @flow
 
const Hoge = ({
  label = '',
  to = '',
  onClick = () => {},
  item = {},
}: {
  label: string,
  to: string,
  onClick: (item: {}, e: Event) => void,
  item: {},
}) => {
...
};
```

上記のような分割代入も可能ですが、下記のように`type Props = {}`と定義することも可能になりました。

```js
// @flow
type Props = {
  label: string,
  to: string,
  onClick: (item: {}, e: Event) => void,
  item: {},
}

const Hoge = (props: Props) => {
...
};

Hoge.defaultProps = {
  label: 'hogehoge',
  to: 'hoge.com',
  onClick: () => {},
  item: {},
}
```

## Component
~~通常のComponentの場合、下記のような書き方で `props` と `defaultProps` を定義する。~~
`defaultProps`の型定義は書かなくて良くなりました。
https://flow.org/en/docs/react/components/

```js
// @flow


type Props = {
  children: React.Element<any>,
};
 
class Hoge extends Component<Props> {
  static defaultProps = {
    children: null,
  };
 
  constructor(props: Props) {
    super(props);
    (this: any).handleBar = this.handleBar.bind(this);
  }
...
}
```

stateにも型定義したいときは、下記のように書く。
https://flow.org/en/docs/react/components/#toc-adding-state

```js
// @flow
 
type Props = {
  children: React.Element<any>,
};
 
type State = {
  foo: boolean,
}
 
class Hoge extends Component<DefaultProps, Props, State> {
  static defaultProps = {
    children: null,
  };
 
  state = {
    foo: true,
  }
...
}
```

この場合、constructor内にthis.stateは書かない。

### DefaultPropsの扱いについて
DefaultPropsを定義しない場合、そのpropsは省略できない(isRequiredと同じような扱いになる)。
https://flow.org/en/docs/react/components/#toc-using-default-props

## 主な要素の型定義の書き方
| 要素タイプ | flow書き方 |
|:--|:--|
|文字|`string`|
|数字|`number`|
|配列(型あり)|`Array<number>`|
|配列(型なし)|`Array<any>`|
|配列(オブジェクト)|`Array<{ ... }>`|
|オブジェクト|`{}`|
||`Object`(中身の型を無視したいとき)|
|関数	|`( e: Event ) => void`|
||引数の型定義も必須|
|ReactElement|`React.Element<any>`|
|イベント|`SyntheticEvent<T>`|


その他型定義は下記にまとまっている。
https://flow.org/en/docs/types/
https://flow.org/en/docs/react/types/

イベントの型定義はこちら
https://flow.org/en/docs/react/events/

# その他
#### **constructorでbindしているthisの書き方**

```js
constructor(props: Props) {
  super(props);
  (this: any).handleBar = this.handleBar.bind(this);
}
```

#### **`this.hoge`の定義**
props, state, メソッド以外のthis.hogeはコンストラクタ、ライフサイクルメソッドの下で型定義する。

```js
constructor(props: Props) {
...
}
 
hoge: string
 
this.hoge = 'hogehoge';
```

#### **Component下部にあるdispatch**

```js
const mapDispatchToProps = (dispatch: Dispatch<*>) => (
  {
    actions: bindActionCreators(Actions, dispatch),
  }
);
```

#### **event.currentTarget**
~~flowはcurrentTargetを直接参照できないので、型定義を挟む必要がある。~~
`SyntheticEvent<T>`でEventの`event.currentTarget`の型定義できるようになりました！！！
 `HTMLButtonElement` の部分はイベントを取得する要素に応じて変更する。
参考：https://flow.org/en/docs/react/events/

```js
handleChange(e: SyntheticEvent<HTMLButtonElement>) {
  const data = { [e.currentTarget.name]: e.currentTarget.value };
  this.props.actions.editEnterpriseInfo(data);
}
```

#### **Maybe Type**
通常、flowではnullとundefinedは許容されない。
必要な部分はMaybeTypesを使用してnullとundefinedを許容できるようにする。
また、オブジェクトのプロパティを省略したいときはプロパティ名のほうに?をつける。(undefinedは可だけどnullは不可)
https://flow.org/en/docs/types/maybe/
https://flow.org/en/docs/types/primitives/#toc-optional-object-properties

```js
// null, undefinedを許容
hoge: ?string
 
// 省略可能なオブジェクトのプロパティ
hoge: {
  foo?: string,
}
```

#### **Globalな変数**

```js
declare var hoge: any;
```

# まとめ
細かい部分でわからないことが多くつらかった。