Flowでの型チェック環境を設定する

flow.org

ちょっと前に、redux-formのリポジトリで「🎉 Flow Typing 🎉 (#3138)」と絵文字まじりのコミットログを見つけて気になって(絵文字を)調べたついでから、JSの型チェックを行うFlowを知りました。型はコードの堅牢性を高めると信じてるので大好きなのですが、Alt-JSとして例えばTypeScriptはやるならとことんやらんといかんというところで乗り換えるに既存の継承でいろいろ面倒なことがあって断念していました。しかしこのFlowはBabelトランスパイル環境に個別ファイル単位で段階的に加えていけるというのが気軽でよいし、もともとFacebook発なReactエコシステム出身でReactへの適用が十分に意識され、React本体周辺でも使い始めていたことから導入意欲が超わきました。

$ npm install --save-dev flow-bin
+ flow-bin@0.53.1

# 私の環境ではdirenvで/node_modules/.binにパスを通してあります
$ flow init
internal/child_process.js:325
    throw errnoException(err, 'spawn');
    ^
Error: spawn EACCES
    at _errnoException (util.js:1041:11)
    at ChildProcess.spawn (internal/child_process.js:325:11)
    at exports.spawn (child_process.js:493:9)
    at Object.<anonymous> (/Users/masataka_k/Projects/bookreader/node_modules/flow-bin/cli.js:17:3)

いきなりのぶっ込みでエラー。JSツールにあるまじき実行権限エラーなのでなにかと思えば、バイナリファイルがflow-binの中にありました。

# ユーザー実行権限付与
$ chmod u+x ./node_modules/flow-bin/flow-osx-v0.53.1/flow

環境別に、以下がありました。私はOSXなのでそれだけchmod u+xです。

  • ./node_modules/flow-bin/flow-linux64-v0.53.1/flow
  • ./node_modules/flow-bin/flow-osx-v0.53.1/flow
  • ./node_modules/flow-bin/flow-win64-v0.53.1/flow.exe

そしてFlowを実行!./node_modules/.bin/flow が flow-bin/cli.jsのシンボリックリンクで、そこから巡り巡って上記の実行権限付与したバイナリを動かします。

$ flow
Launching Flow server for /Users/masataka_k/Projects/bookreader
Spawned flow server (pid=17385)
Logs will go to /private/tmp/flow/zSUserszSmasataka_kzSProjectszSbookreader.log
Error: node_modules/react-event-listener/src/index.js:49
 49:   children?: React.Element<any>,
                  ^^^^^^^^^^^^^^^^^^ Element. Property not found in

初回に少々時間かかってると思いきや、node_modulesにチェック行っちゃってました。flow initで作られている.flowconfigを書き換えます。ignoreよりは対象フォルダを設定するほうが検索減らしやすくていいので、後々の課題としておきます。

# .flowconfig
[ignore]
.*/node_modules/*

[include]

[libs]

[lints]

[options]

Babelのビルドに問題生じないように、プリセット設定にbabel-preset-flowを追加。Eslintにも、eslint-plugin-flowtypeを追加。run-scriptも調整で、lintの末尾でflow実行するようにしました。もちろんwebpack.config.production.jsに含まれるBabel設定にもプリセット追加済み。

// package.json
  "scripts": {
    "start": "webpack-dev-server",
    "test": "cross-env BABEL_ENV=JEST jest",
    "build": "rimraf ./static/js/*; webpack -p --config webpack.config.production.js",
    "lint": "eslint --ext [.jsx,.js] js; flow check"
  },
  "babel": {
    "presets": [["es2015", {"modules": false}], "react", "stage-1", "flow"],
    "plugins": ["transform-decorators-legacy", "react-hot-loader/babel"],
    "env": {
      "JEST": {
        "plugins": ["transform-es2015-modules-commonjs"]
      }
    }
  },
  "eslintConfig": {
    "parser": "babel-eslint",
    "env": {
      "browser": true,
      "jest": true
    },
    "extends": ["airbnb", "plugin:flowtype/recommended"],
    "plugins": ["flowtype"],
    "rules": {
      "indent": ["warn", 4, {"SwitchCase": 1}],
      "max-len": ["warn", 120],
      "react/jsx-indent": ["warn", 4],
      "react/jsx-indent-props": ["warn", 4]
    }
  },

Using Flow in WebStorm | WebStorm Blog

愛用のWebStormではすでにFlowに対応していました。言語バージョン設定が、ECMAScript 3 -> ECMAScript 5.1 -> JavaScript 1.8.5 -> ECMAScript 6 -> React -> Flow と進んでいくのを見るとFlowはReactの(JSやESの、ではない)正統進化ストリームとして認められてるのでしょうか?

f:id:masataka_k:20170830035658p:plain

Flowの環境設定はこれで完了。すごいぞWebStorm!すごいぞFlow!型チェックで出したエラーが即時エディタにマーカー出し(numberであるべき引数にstringを渡している)してくれる。もちろん同時にeslintも走って(ダブルクォーテーションを嫌ったエラーを)同じところで出してる。

f:id:masataka_k:20170830062952p:plain

次いで型活用へ。とりいそぎ感服したやつを列挙。ほかにコールバックの関数型を縛ったり、オブジェクトプロパティを縛ったりするのも強力。