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
で絶対パスが使用できるように設定している。
[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を使えるようにする。
"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で型チェックをしたいファイルの上部に下記のようなコメントアウトを入れる。
// @flow
Functional Component
Functional Componentの場合、下記のような書き方で props
と 型
と defaultProps(必要なら)
を定義する。
https://flow.org/en/docs/react/components/#toc-stateless-functional-components
この書き方はES2015の分割代入を使っている。
// @flow
const Hoge = ({
label = '',
to = '',
onClick = () => {},
item = {},
}: {
label: string,
to: string,
onClick: (item: {}, e: Event) => void,
item: {},
}) => {
...
};
上記のような分割代入も可能ですが、下記のようにtype Props = {}
と定義することも可能になりました。
// @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/
// @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
// @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の書き方
constructor(props: Props) {
super(props);
(this: any).handleBar = this.handleBar.bind(this);
}
this.hoge
の定義
props, state, メソッド以外のthis.hogeはコンストラクタ、ライフサイクルメソッドの下で型定義する。
constructor(props: Props) {
...
}
hoge: string
this.hoge = 'hogehoge';
Component下部にあるdispatch
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/
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
// null, undefinedを許容
hoge: ?string
// 省略可能なオブジェクトのプロパティ
hoge: {
foo?: string,
}
Globalな変数
declare var hoge: any;
まとめ
細かい部分でわからないことが多くつらかった。