Javascriptを静的型付でかけるFlowの使い方

概要

  • FLOW
  • JavaScriptコードの静的型チェッカー
  • 静的型アノテーションを使用してコードのエラーをチェック
  • babel を使用する方法と flow-remove-types というモジュールを使用する方法がある
  • 今回は babel を使用して webpack でバンドルする感じで使う

Flow単体で使ってみる

インストール、準備

  • 1
    $ yarn add -D babel-cli babel-preset-flow
  • .babelrc ファイルを作成

    • 1
      2
      3
      {
      "presets": ["flow"]
      }
  • package.json の “scripts” に下記追加

    • このコマンドで src ディレクトリ直下のファイルをコンパイルして lib ディレクトリに作成できる
    • -d = 別ディレクトリに作成するオプション
    • 1
      2
      3
      "scripts": {
      "build": "babel src/ -- -d lib/"
      },
  • Flowをインストール

    • 1
      $ yarn add --dev flow-bin
  • VSCodeで書くのでついでに以下のプラグインもインストールしました

    • vscode-flow-ide
    • low Language Support
      • 型指定すると「TSファイルしか型指定できない」みたいなエラーがずっと出てしまうので UserSettings で “javascript.validate.enable”: false にした。ただjavascriptのバリデーションされなくなるので応急処置… eslint とかと一緒に使ってれば問題ないでしょうか…
  • .flowconfig ファイルの作成

    • 1
      $ yarn run flow init
  • .flowconfigに下記を追加

    • 1
      2
      [ignore]
      .*/node_modules/.*

使い方

  • ファイルの先頭に「 // @flow 」を記述してチェックの対象にする
  • 以下だと、返り値に string を指定してるのに、numberが帰ってくる可能性があるのでエラーになる
  • エディタにFlowIDEのプラグイン入れているとこの時点でエラーを教えてくれる

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // @flow

      hoge = (x: ?number): string => {
      if (x) {
      return x;
      };
      return "default";
      };

      hoge("q");
  • flow コマンドを打てばflowファイルを全て検証してくれる

    • 1
      flow
  • 上のファイルは以下のように引数の型をstringにして引数に渡す値もstringにすれば解決する

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // @flow

      const hoge = (x: ?string): string => {
      if (x) {
      return x;
      };
      return "default";
      };

      hoge("2");

型の種類

  • TypeSystem
    • 細かい型の種類とかは上記を見ながらコード書きながら覚えます
    • genericsとかあるしjavaのTypeSystemに近い感じがしました(javaのTypeSystemそこまで知りませんが)

eslint + prettier + webpack との使用

  • eslint用のプラグインインストール

    • 1
      $ yarn add -D eslint-plugin-flowtype
  • .eslintrcに下記追加

    • 1
      2
      3
      4
      5
      6
      "parser": ["babel-eslint"],
      "plugins": ["prettier"],
      "rules": {
      "flowtype/define-flow-type": 1,
      "flowtype/use-flow-type": 1
      }

reduxと使う場合

  • flowのバージョンに対応するredux用の型を探す

    • 1
      $ yarn run flow-typed search redux
    • こういうのが出てくるのでflow-binのバージョンにあったものを追加

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      ╔══════════════════════════╤═════════════════╤═════════════════════╗
      ║ Name │ Package Version │ Flow Version ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-form │ v5.x.x │ >=v0.25.x <=v0.52.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-form │ v5.x.x │ >=v0.53.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ react-redux │ v5.x.x │ >=v0.63.0
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ react-redux │ v5.x.x │ >=v0.54.x <=v0.62.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ react-redux │ v5.x.x │ >=v0.53.x <=v0.53.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ react-redux │ v5.x.x │ >=v0.30.x <=v0.52.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux │ v4.x.x │ >=v0.55.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ react-redux │ v4.x.x │ >=v0.30.x <=v0.52.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ react-redux │ v4.x.x │ >=v0.53.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-oidc │ v3.x.x │ >=v0.55.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux │ v3.x.x │ >=v0.28.x <=v0.32.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux │ v3.x.x │ >=v0.33.x <=v0.54.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux │ v3.x.x │ >=v0.55.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux │ v3.x.x │ >=v0.25.x <=v0.27.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-loop │ v2.2.x │ >=v0.33.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-actions │ v2.x.x │ >=v0.34.x <=v0.38.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-devtools-extension │ v2.x.x │ >=v0.47.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-actions │ v2.x.x │ >=v0.39.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-mock-store │ v1.2.x │ >=v0.25.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-rsaa-middleware │ v1.x.x │ >=v0.66.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-act │ v1.x.x │ >=v0.39.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-actions │ v1.x.x │ >=v0.25.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-act │ v1.x.x │ >=v0.34.x <=v0.38.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-saga │ v0.16.x │ >=v0.56.0
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-observable │ v0.14.x │ >=v0.47.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-saga │ v0.13.x │ >=v0.28.x <=v0.37.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-saga │ v0.13.x │ >=v0.38.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-forms-react │ v0.11.x │ >=v0.34.x <=v0.52.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-forms │ v0.11.x │ >=v0.34.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-saga │ v0.11.x │ >=v0.38.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-saga │ v0.11.x │ >=v0.28.x <=v0.37.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-forms-react │ v0.10.x │ >=v0.34.x <=v0.52.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-forms │ v0.10.x │ >=v0.34.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-actions │ v0.9.x │ >=v0.25.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-saga-thunk │ v0.5.x │ >=v0.25.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-exr │ v0.4.x │ >=v0.65.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-entitize │ v0.3.x │ >=v0.45.x ║
      ╟──────────────────────────┼─────────────────┼─────────────────────╢
      ║ redux-entitize │ v0.2.x │ >=v0.45.x ║
      ╚══════════════════════════╧═════════════════╧═════════════════════╝
    • 追加

      • 1
        $ yarn run flow-typed install redux@v4
  • babelにプラグイン追加

    • 1
      $ yarn add -D babel-plugin-transform-flow-strip-types
  • .babelrc

    • 1
      2
      3
      4
      {
      "presets": [["env"],["react"]],
      "plugins": [["transform-flow-strip-types"]]
      }

Flowを使っている時CSSを読み込んでいても解決できないと言われる

  • 拡張子が読み込めてないことが問題らしい(面倒くさい…)
  • .flowconfig に読み込める拡張子を追加する

    • 1
      2
      [options]
      module.file_ext=.styl //stylusを使っていたので
  • jsファイルでcss読み込むときは変数に格納せずにimportすると、「never use ~」みたいなエラーを防げる

    • 1
      import './assets/style.styl';

感想

  • 面倒くさい
  • 絶対Typescriptの方がいい気がする
  • Flowのメリットとしては通常のJSに戻すこともできるということ
  • 個人的にはTS使いたいけど諸々の事情でFlow学習する
    • TSは趣味ででも…

参考