ということで、前回に引き続きReact Confで紹介されてたものを試すシリーズの第二回です。
flowの簡単な設定とeslint/babel/precommitフック/vimとの連携のみです。深い使い方は(今のところは?)書きません。flowは既に色々なところで紹介されてますが、気にせず行きます。
ちなみに今回もReactは出てきません。(追記で少しだけ触れてしまいました)
flowとは
- JavaScript用のtypeチェッカー
- ファイル単位で適用できるため、既存のプロジェクトにも気軽に導入できる
- eslintやbabelとも連携できるので既存プロジェクトに導入しやすい
- 色んなエディタ/IDEでもサポートされている
- この辺を見る限りFacebookのデベロッパ的にはTypeScriptはあんまり推奨してないっぽい
- GitHubのstarヒストリーを見る限りはTypeScriptに分がありそう
- 数日程度弄った限りだと基本的なシンタックスは凄く簡単。でも複雑なことも色々できる模様。
使い方
前回eslintとprettierを導入した環境に追加していきます。
ソースはここにあります。便宜上ディレクトリは分けました。
単体で使う
まずはflowをインストールして初期化します。
yarn add -D flow-bin
flow init
空の.flowconfigができるので、以下のようにnode_modulesをチェック対象外にします。
[ignore]
.*/node_modules/.*
[include]
[libs]
[options]
テスト用にエラーになるjsファイルを作ってみます。
/* @flow */
var a: number = 1;
a = 'string';
先頭のコメントはflowのチェック対象にするために必要です。
この状態でflow check
コマンドを実行するとnumber
で宣言した変数にstring
を入れようとしているので、エラーになります。
$ flow check
index.js:5
5: a = 'string';
^^^^^^^^ string. This type is incompatible with
3: var a: number = 1;
^^^^^^ number
Found 1 error
babelと連携する
babelと連携するにはbabel-plugin-transform-flow-strip-typesが必要です。
babel自体とes2015のpresetと一緒に導入してみます。
yarn add -D babel-cli babel-preset-es2015 babel-plugin-transform-flow-strip-types
そして以下のような内容で.babelrcを作ります。
{
"plugins": ["transform-flow-strip-types"],
"presets": ["es2015"]
}
そしてjsをletを使うように変更してbabelを実行してみます。
/* @flow */
let a: number = 1;
a = "string";
babel index.js --out-dir dist
出力されたindex.js
を見ると適切に変換されているのがわかります。
"use strict";
var a = 1;
a = "string";
eslintと連携する
このままだと: number
などのTypeアノテーションがeslintのチェックを通りません。
eslintと連携するにはbabel-eslintとeslint-plugin-flowtypeをインストールする必要があります。
yarn add -D eslint-plugin-flowtype babel-eslint
.eslintrc.jsに以下を追加します。中身は明らかなので説明は要らないかと思います。
parser: 'babel-eslint',
extends: ['plugin:flowtype/recommended'],
plugins: ['flowtype'],
settings: {
flowtype: {
onlyFilesWithFlowAnnotation: true,
},
},
precommitフック
前回作ったprecommitフックにflowを追加して、flowのtypeチェックが失敗したらコミットをabortするようにします。
"scripts": {
"precommit": "lint-staged && flow check"
},
この状態でflowでエラーになるファイルをコミットしようとすると、エラーを吐いて止めてくれます。
$ git add index.js
$ git commit -m 'flow test'
> husky - npm run -s precommit
↓ Running tasks for gitDir [skipped]
→ No staged files match gitDir
✔ Running tasks for *.js
index.js:5
5: a = "string";
^^^^^^ string. This type is incompatible with
3: let a: number = 1;
^^^^^^ number
Found 1 error
> husky - pre-commit hook failed (add --no-verify to bypass)
> husky - to debug, use 'npm run precommit'
js以外のリソースに対応する
js以外のリソース(フロントだとcss、バックエンドだとsql等)があるとRequired module not found
というエラーが出てしまいます。
こんなときは以下のようなファイルを作って、
/* @flow */
declare export default string
.flowconfig
のoptions
に以下を追記すると解消できます。
[options]
module.name_mapper.extension='sql' -> '<PROJECT_ROOT>/flow/SQLAsset.js.flow'
エディタ連携
色々なエディタがサポートされてるようですが、とりあえずvim連携を試します。これは公式アカウントが提供しているvim-flowを入れればOKです。
以下はdeinの例
[[plugins]]
repo = 'flowtype/vim-flow'
on_ft = ['javascript', 'javascript.jsx']
flowのパスは適宜init.vimや.vimrc等で設定してください。
let g:flow#flowpath = l:bin_folder . 'flow'
let g:flow#autoclose = 1 " エラーを表示するquickfixのウィンドウをエラーがなくなり次第閉じる
これで保存時に自動でflowのチェックが走り、エラーがあるとquickfixウィンドウにエラーを表示してくれます。
シンタックスハイライトはvim-javascriptが対応しているので、以下を追加するだけでOKです。
let g:javascript_plugin_flow = 1
追記
flow-typed(2017/03/25)
.flowconfigでnode_modules全体をignoreに指定して外すとnode_modules内のモジュールをimportしたときにエラーになります。
これを解決するにはflow-typedを使います。
一部のライブラリは既にflow-typedを通じて型情報を提供しているので、yarn add -D flow-typed
してからflow-typed install
するとpackage.jsonで指定したライブラリのうちflow-typedに対応している型情報をflow-typedディレクトリに入れてくれます。また対応していないものもany型でのstubが作成されるためflowがエラーを吐かなくなります。なお、これはデフォルトで解決できるようになるので、.flowconfigの[libs]セクションにflow-typedディレクトリを明示的に指定する必要はありません。
yarn add -D flow-typed
flow-typed install
PropTypesをflowに変換する(2017/5/1)
Reactv15.5.0からPropTypesが別パッケージになりました。
PropTypes使うよりもflowでチェックした方が実行前に型チェックができるのでより安全です。codemod-proptypes-to-flowを使うとPropTypesをflowに自動的に変換してくれます。いくつかの制約はありますが、今後はある程度の制約を受け入れてflowに移行したほうが良いのではないかと思います。インストールして実行するだけなので手順は省略します。
カバレッジを取得(2017/5/1)
flow-coverage-reportを使うとflowのtypeのカバレッジを取得することができます。これまたインストールして実行するだけなので手順は省略します。
最後に
以上、参考になれば幸いです。
ちなみにprettierはデフォルトでflowに対応しているので、特に設定不要です。