Facebookが2014/11/18に公開したJavascriptの静的型付けチェッカー「Flow」を試してみた。
Flowの概要
FlowはOCamlで実装されたJavaScript向けのデバッグツール。データ型の違いによって生じる問題を解決するために開発したという。コード中に型情報を埋め込むことなしに事前の型チェックを行えるほか、コード中に明示的に型情報を記述して使用することも可能。これにより、JavaScriptコードを段階的に型付きのコードに書き換えて行くこともできるという。
型情報の記述には独自のフォーマットを使用しているが、簡単に通常のJavaScriptコードに変換できるという。そのため、Flow向けに記述したコードは容易にさまざまな環境で実行できるという。
Facebook、JavaScript用の静的型付けチェッカー「Flow」をリリース | SourceForge.JP Magazine
ちなみに自分は今流行のTypeScriptは使ったこと無いので、静的型付けの部分の比較はできません。あしからず。
今度TypeScript試します。
インストール
Getting started with Flow を見てもらうと分かる通り、Flowの公式サイトでバイナリが配布されている。
zipファイルをダウンロードしてもいいし、Homebrewでも配信されているのでHomebrewでインストールする方が楽か。
# バイナリデータの場合はunzipしてflowコマンドを叩けるようにパスを通す
$ unzip flow.zip
$ cd flow
$ echo "PATH=\"\$PATH:$(pwd)/\"" >> ~/.bashrc && source ~/.bashrc
# Homebrewでインストールする場合
$ brew install flow
# インストールされたか確認
$ flow --version
Flow, a static type checker for Javascript, version 0.1.1
ダウンロードしたバイナリにパスを通す、またはHomebrewでインストールするとflowコマンドが使えるようになる。
Flowを使う
Getting startedを読みつつ、さっそく使ってみる。
まずはJSファイルをチェックする
flow check
で型付けチェックができるようなので、
あらかじめチェック対象のJSファイルを用意しておく。
/* @flow */
function foo(x) {
return x * 10;
}
foo('Hello, world!');
こちらもGetting startedに載ってるJSファイルを使う。
文字列に数値で計算しても実行エラー。
1行目には/* @flow */
というコメントを付けておく必要がある。このヘッダーコメントがないとFlowに認識されずチェック対象から外れる。
ではチェックしてみる。
$ cd path/to/flow/
$ flow check
Could not find a .flowconfig in . or any of its parent directories
see "flow init --help" for more info
怒られた。
.flowconfig
ファイルが必要だ。チェック対象のJSファイルがあるディレクトリもしくは親のディレクトリに置いてね、とのこと。
.flowconfigはflow init
で作成できます。pathオプションを指定すれば、指定したディレクトリに作成することも可能。
# .flowconfigを置くディレクトリパスを指定
$ flow init ../
実行した感じとしては同じディレクトリに.flowconfigを配置したほうが実行が速く感じた。
また、サンプルファイルを覗いてみたけど.flowconfigの中身は空でも良さそう。
しかしファイル自体が存在しないと動いてくれないのが注意すべき点。
.flowconfigを作成してもう一度flow check
を実行する。
$ flow check
/path/to/flow/hello.js:8:5,19: string
This type is incompatible with
/path/to/flow/hello.js:5:10,15: number
Found 1 error
動いた。number型のところをstring型で入力してるぞ、と言っている。賢いな。
しかしエラー出力がわかりづらい。
明示的に型を指定する
Flowは空気を読んで型チェックしてくれるけども、明示的に型を指定することもできる。
関数の引数と返り値に型を設定して実行してみる。
/* @flow */
function foo(x: string, y: number): string { // 返り値をstringとして指定
return x.length * y;
}
foo('Hello', 30);
$ flow check
/path/to/flow/foo.js:4:10,21: number
This type is incompatible with
/path/to/flow/foo.js:3:37,42: string
Found 1 error
返されるべき値はnumberだと言っている。こいつは賢い。
ちなみにBaseTypeとして以下の型を見てくれる。
- number
- string
- boolean
- void ※voidはundefined
- constructor ※クラス型
- mixed ※ 全てのスーパー型
- any ※ 動的型
- Array ※ 後述
あなたが自分のプログラムに絶対的な確信が持てて、Flowの型付けチェックをスルーしたい場合はany
を指定してね。でも危険だからね、自分で責任持てよな、とのこと。
使いどころは例えばオブジェクトリテラルを返す場合など。
/* @flow */
function Person(name: string): any {
this.name = name;
this.say = function (): string {
return 'My name is ' + this.name;
}
return {
name: this.name,
say : this.say
};
}
// 変数の型指定はこう。constructor型を指定。
var john: Person = new People('John');
john.say();
Nullには厳しい
/* @flow */
function length(x) {
return x.length;
}
var total = length('Hello') + length(null);
$ flow check
/path/to/flow/null.js:4:10,17: property length
This type is incompatible with
/path/to/flow/foo.js:7:38,41: null
Found 1 error
length(null)
はTypeErrorでnullのlengthは読み取れない。
配列
配列はArray<[type]>
として配列に格納されている値の型を指定できる。
/* @flow */
function total(numbers: Array<number>) {
var result = 0;
for (var i = 0; i < numbers.length; i++) {
result += numbers[i];
}
return result;
}
total([1, 2, 3, 'Hello']);
$ flow check
/path/to/flow/null.js:11:17,23: string
This type is incompatible with
/path/to/flow/foo.js:3:31,36: number
Found 1 error
Array<number>
と型指定しているのにstringの'Hello'が配列に入っている。
動的なコード
2つの異なる型を引数に指定してみる。
/* @flow */
function foo(x) {
return x.length;
}
var res = foo('Hello') + foo(42);
$ flow check
/path/to/flow/null.js:4:10,17: property length
This type is incompatible with
/path/to/flow/foo.js:50:1,62:1: Number
Found 1 error
2回目のfoo()
呼び出しでは数値が指定されていて、lengthプロパティにアクセス出来ない。
Flowサーバ
Flow内部にサーバを備えている。
しかし、オートマチックに監視してターミナルに出力してくれるものではなく、flowコマンドを叩く必要がある。
flow check
ではなく flow
で動作します。
# Flowサーバ起動
$ flow start
# check
$ flow
# Flowサーバ停止
$ flow stop
Flowのサーバを起動すると毎回の実行にかかるオーバーヘッドが減るから速いんだよ、というお話。
watchしてファイルが変更されたらflowコマンドを叩く、というのがいいかも。
使ってみた雑感
- 既存のjsコードのチェックツールとして使いやすそう。
- その他に、JSXに対応していたりするので組み合わせて使ってもいいですね。Running Flow code
- npmなどで配布されていないということや、Gruntやgulpで使えないのが個人的にはつらいところ。
- あんまりFlowとは関係ないけど、初めて型指定の記述を書いてみたら、意外とこれは明示的でわかりやすくて良いと思った。
- なので今度はTypeScript試す。