kakts-log

programming について調べたことを整理していきます

facebook/flow Comment Typesによるpure javascriptでの型チェック

概要

javascriptの静的型付けのチェックを行うツールであるflowというツールを導入し、業務で開発しているプロダクトの品質向上を目指そうと考えています。
既にpure javascriptで書かれた環境があるので、このコードを書き直さずにflowを導入したいと考えており、導入方法について調べた内容をまとめます。
github.com

flowを使った従来の型チェック

一般的に、flowを使ってjavascriptコードの型チェックを行うためには、flow独自のsyntaxでコードを記述し、 実環境へのデプロイは、babelを使ってpure javascriptのコードへトランスパイルするのが一般的です。

// flow独自のsyntaxで記述
// @flow
function square(n: number): number {
  return n * n;
}

square("2"); // Error!
既に本番運用されているプロダクトへのflowの導入の辛さ

しかし、既にpure javascriptで記述して運用されている実環境において、flowを導入するために、イチからflow syntaxで書き直すのは現実的ではありません。
デプロイ処理にbabelを使った pure javascriptへのトランスパイルを挟む必要があるし、テスト周りの処理も見直さなければなりません。
そもそもトランスパイルを挟むなら、型指定のあるtypescriptにまるっとコードを置き換えたほうが良いかと思います。

型チェックのために、コードをflow用に書き直すことなく、pure javascriptでflowの型チェックの利点を活かす事ができれば、既に運用されているnode.jsプロダクトへ楽に導入できるし、プロダクトの品質向上につながります。

私と同じように考えている人は多いらしく、本家のissueや、stackoverflowにも幾つか言及がありました。 github.com

stackoverflow.com

Comment Typesを使った型チェック

前述した要望を見たすために、flowは公式にコメントベースでのsyntaxをサポートしています。
このsyntaxを使うことで、pure javascriptのコードを変えずに、クラスプロパティや、関数、メソッド引数の型チェックが可能になります。
babelによるトランスパイルを挟まなくても型チェックを導入できるので非常に便利です。
flow.org

Comment type include

このComment Typesを使う際には、従来のコメントと区別するために、コメントの先頭にダブルコロン「::」を付ける必要があります。

/*::
type Foo = {
  foo: number,
  bar: boolean,
  baz: string
};
*/

class MyClass {
  /*:: prop: string; */
}

上記のコードは、pure javascriptであり、従来通り実行が可能です。 flowによる型チェックの際に、以下のような形でインクルードされ、型チェックが行われます。

type Foo = {
  foo: number,
  bar: boolean,
  baz: string
};

class MyClass {
  prop: string;
}

ダブルコロンの他に 「flow-include」と記述しても動作します。

/*flow-include
type Foo = {
  foo: number,
  bar: boolean,
  baz: string
};
*/

class MyClass {
  /*flow-include prop: string; */
}
Comment type annotation

前項のように毎回::やflow-includeを使って書く代わりに、コメントの先頭にシングルコロン「:」を使って楽に書くことができます。

例えば下記のように、関数の引数のあとに追加します。

function concat(a /*: string */, b /*: number*/) {
  return a + b;
}

// 引数の型がそれぞれ指定したものと異なっている
concat(1, "a")

flowによる型チェックを実行すると

Error: index.js:24
 24: concat(1, "a")
            ^ number. This type is incompatible with the expected param type of
 13: function concat(a /*: string */, b /*: number*/) {
                           ^^^^^^ string

Error: index.js:24
 24: concat(1, "a")
               ^^^ string. This type is incompatible with the expected param type of
 13: function concat(a /*: string */, b /*: number*/) {
                                            ^^^^^^ number

ちゃんと型チェックが行われ、エラーを出してくれます。

まとめ

flowによる型チェックは、引数のnullチェックなどちゃんとやってくれるようで非常に強力です。
これを実環境のコードやデプロイフローを変えるなく導入できれば、コードのさらなる品質向上が期待できます。

前述したように、flowはComment Typeで、コメントとして記述する方法もちゃんとサポートされているので、 導入するハードルがかなり下がるので、pure javascriptで書いているプロジェクトは導入する余地がかなりあると思います。