tomoima525's blog

Androidとか技術とかその他気になったことを書いているブログ。世界の秘密はカレーの中にある!サンフランシスコから発信中。

Flow導入して数ヶ月がたった所感

f:id:tomoima525:20171003132858p:plain
ReactNativeプロジェクトで、型がないことによるつらいシーンが多くなり(特に変数の解釈に起因するバグ)、Facebook製の静的型解析ツールであるFlowを数ヶ月前に導入しました。導入時の学びと、しばらく運用して感じていることについて個人的な感想を書きました。

Flow選定理由

Javascriptで静的な型付けをするといえばTypeScript(正確にはJavascriptのスーパーセット)がありますが、プロジェクト途中からの導入しやすさの観点からFlowにしました。Flowはお作法(行頭に @flow つける等)さえ押さえれば誰でも使えることから導入障壁はだいぶ低いといえます。導入のメリットについては以下のスライドがとてもわかりやすいです。

speakerdeck.com

flow status でプロジェクトに対して静的型解析を走らせることもできますが、 コーディング時にワーニングを出す方が便利です。Atomを使っている場合、Nuclideを導入し、Flowの設定をするだけです。

導入時の学び

Flowのドキュメントに従うと手こずる

素直にFlowのinstallationドキュメントを参考にすると、node_module配下のflowアノテーションが付いたファイルも解析されてしまい大量のエラーが出ました。実はそもそもReactNativeは標準でFlowに対応しているため、Flowのドキュメントを参考にする必要がありません(むしろうまくいきません)。完全に罠ですね。

ReactNatveでFlowを導入する場合は、以下のようにします。

  • rootディレクトリにある.flowconfigの中身をみてflowのversionを確認する
[ignore]
; We fork some components by platform
.*/*[.]android.js
...
[version]
^0.38.0
  • yarnやnpmでflowを追加します
npm i [email protected] --save-dev

これだけです。

参考:
medium.com

なお、もし無理やりFlowのinstallation手順で行く場合は、 .flowconfig にいくつかの設定を行いnode_module/配下を解析対象から外すように出来ます。

[ignore]
; Ignore templates for 'react-native init'
.*/local-cli/templates/.*

; Ignore "BUCK" generated dirs
  <PROJECT_ROOT>/\.buckd/
            
.*/Libraries/react-native/React.js
.*/Libraries/react-native/ReactNative.js   
; Ignore react related flow in node module
.*/node_modules/react-native.*/.*
.*/node_modules/fbjs/.*

上記の場合、Moduleが認識されなくなるのでreact-nativeのModuleについてStubを作る必要もあります。

declare module 'react-native' {
  declare var exports: any
}

参考:
github.com github.com

一部のモジュールが認識されなかった

例えばフォーマット関数を持つIntlクラスはオブジェクトとして認識されず、エラーが出ます。

const mmyy = new Intl.DateTimeFormat(ENV.LOCALE,...)
                 ^^^^ identifier `Intl`. Could not resolve name

こういったオブジェクトは型を明示的に定義して上げる必要があります。

type IntlType = any;
const intl: IntlType = window.Intl;

const mmyy = new intl.DateTimeFormat(ENV.LOCALE,...)

参考: github.com

数ヶ月運用した所感

Flowを使い始めてしばらくたった個人的な感想をつらつらと書きます。

良かった点

スピーディな型チェック

コーディング中に型チェックが行われるのでまずい実装をしているとすぐわかります。また↓のように定数の打ち間違いなども適宜指摘してくれます。いくつかの潜在的なバグが洗い出されたのは良かったです。

f:id:tomoima525:20171003132624p:plain
PhoneVerificationTypeにreset_to_initial_stateというPropertyが定義されてないと教えてくれている

ラーニングコストの低さ

導入も含め、ラーニングコストが低いです。型の作り方も初歩的なものであれば容易に理解できますし、TutorialもReactやReduxで導入する方法など充実してます。なおTypeScriptはDocumentをちょろっと読んだ程度なので、比較はできてないです。

課題点

誰かが率先していかないと導入は進まない/効果は低い

Flowはあくまでもコーディング中にWarningを出すだけなので、強制力はありません。@flowアノテーションをつけないとWarningもそもそも出ません。なので、チームがメリットをよく理解した上で率先して導入していかないと、導入率は上がらないです。その点で言うと型を強制するTypeScriptの方が良いかもしれません。

バニラなJavascriptではなくなり、コード量が増える

当たり前な話なのですが。FlowはJavascriptに型を与えるものなので、正確にはJavascriptとは呼べなくなります。例えば、React ComponentにFlowを適用すると、propTypes等の型定義が必要になります。これにはFlowの特有の記法が求められます。またpropsの型定義を追加すると当然ながら若干コード量が増えます。いざFlowを辞める場合(ないとは思いますが)、このような変化はチーム内で合意を得ておく必要があると思います。

結局全てに導入しないと本当の恩恵は受けられない

例えばあるクラスにFlowを導入した場合、呼び元のクラスがFlowに対応していない場合は当然警告が出ないです。なので導入が進まないと目に見えた効果が出てこないな~と思いました。途中から導入する場合でもガガっと全てにFlowを適用する方がよいのかもしれません。

まとめ

三行でFlowについて思う所を述べるとこんな感じです。

  • 導入は非常に簡単、ラーニングコストは低い
  • 型付けの恩恵を十分に得たいのであれば最初から導入したほうがよい
  • 型を強制するものではないので、チーム全体が意識を持って利用しなければ効果は薄い