静的型チェッカーflowを使ってみて、微妙に気になったこと4つ

flowはJavaScriptの静的型チェックツールの一つ。

同じような静的型関連ツールであるTypeScriptと比較して、ESLintやBabelとの併用がしやすかったり、型付けが強めだったり(初期からnon-nullableに対応)と、受ける恩恵も多い。

が、使っていて「おやっ?」っと思う点もいくつかあったので、まとめてみた。

気になったこと4つ

ESLintとの併用でめんどうな事が多かった

babel-eslintというBabelパーサーを用いれば、概ねのESLintルールがflow文法でも適応できるが、例外もあった。

例えば、インターフェイスの定義の場合、

interface I {
  field: number;
  method(): string;
}

ESLintのno-undefルールを有効にしていると、以下のエラーが発生する。

error| 'I' is not defined. (no-undef)

ESLintの誤爆ではあるが、そもそも、interfaceなんて文法はECMAScriptにはないので、対応しろっていうのも無茶な話。

ESLintのno-undefまわりを無効にすればエラーは出ないが、有志の方が、flow用のBabelカスタムルールを実装してくれているので、ありがたく利用させていただく。

GitHub: zertosh/eslint-plugin-flow-vars
https://github.com/zertosh/eslint-plugin-flow-vars

このBabelプラグインを使ってカスタムルールを有効にすれば、no-undefを無効にすることなく、ESLintとflowを併用できる。

// .eslintrc.jsの修正例
module.exports = {
  "parser": "babel-eslint",
+ "plugins":
+   "flow-vars",
+ ],
  "rules": {
    "no-undef": 1,
+   "flow-vars/define-flow-type": 1,
+   "flow-vars/use-flow-type": 1,
  },
};

その他、いろいろ試行錯誤している時にTwitter上でご教示いただいたPluginも、いくつかのflow用カスタムルールがあった。何かflow+ESLintで不都合があり次第、逐一有効にしていく必要があるかも。

Babelを通しても、ES6文法で使えないものがある

例えば、ES6のget/setプロパティが使えなかった。

イミュータブルなオブジェクトを作って、Privateな値を返すゲッターを作った際、プロパティのように扱えるので、好んで使っていたが、

class A {
  get field(): number {
    return 1234
  }
}

const a = new A()
console.log(a.field)

flowに通すと、怒られる。

error| get/set properties not yet supported

yetってあるので、いずれサポートされるのだろうか。

ジェネリクスなクラス/関数の使い方次第でエラー

flow的にはPolymorphismというのかな?
TypeScriptやJava感覚で以下のように書くと、flowだとSyntaxエラーになった。

const map = new Map<string, number>()
error| Parsing error: Unexpected token

そもそもBabelがエラーを吐く。babel-plugin-transform-flow-strip-typesの限界っぽい。

以下のように書くのがflow流っぽいが、なんか冗長な感じがして、あまり好みじゃない。

// 変数定義で型を決めとく
const map: WeakMap<string, number> = new WeakMap()

// 型のキャスト
const map = (new WeakMap(): WeakMap<string, number>)

関数の場合も似たようなSyntaxエラーを吐くが、 以下の様な型推論が効く例だと、逆にスマートな感じで良い。

function toArray<T>(x: T): Array<T> {
  return [x]
}

// 引数の型(number)を元に、返り値の型(Array<number>)が推論される
const nums = toArray(2)

クラスのフィールド定義などでセミコロンが強制

クラスやインタフェースの定義などで、以下のように書くと、

class A {
  num: number

  constructor(num: number) {
    this.number = num
  }
}

次のエラーが、constructorの行で発生する。

error| Unexpected identifier

フィールド定義の行末尾にセミコロン(;)を入れれば、エラーは出ない。

class A {
- num: number
+ num: number;

  constructor(num: number) {
    this.number = num
  }
}

アンチ・セミコロン派には吐きそうなほどキツイ仕様。
https://github.com/facebook/flow/issues/825

まとめ

flowを使ってみて、以下4つの気になったことをまとめてみた。

  • ESLintとの併用でめんどうな事が多かった
  • Babelを通しても、ES6文法で使えないものがある
  • ジェネリクスなクラス/関数の使い方次第でエラー
  • クラスのフィールド定義などでセミコロンが強制

flowは現在も、定期的にVerUPされているツールなので、今回上げた点もいつの間に修正されているかもしれない。今後も細かく見ていきたい。

comments powered by Disqus

この記事について

書いた人
Written by

namikingsoft

何かを残して逝きたい
フロントエンドエンジニア