ES2015とmochaで開発している環境にflow-typeを入れてみる準備

  • 2
    いいね
  • 0
    コメント

概要

JavaScriptで雑なmoduleを書くことが多いんですが、interfaceとか使えないかなぁと思ってflow-typeを試してみました。
普段はES2015で書いたコードをbabelでcompileしてnpm publishするみたいなことをやっています。
テストにはmochaを利用しているので、この環境に合う感じにflowが導入できると最高みたいな感じです。

https://flowtype.org/
使ったflowのversionは0.31.0です。
nodeのversionは6.5.0です。

やってみたこと

  • Interfaceを定義して使う
  • babelでコンパイルするときにflow-typeの型を消す
  • mochaでテストを回すときにflow-typeの型がついていても動くようにする

今回やってみたもろもろのコードはこちらのレポジトリにおいています。

https://github.com/ara-ta3/flow-type-with-babel-and-mocha

Interfaceを定義して使う

この辺を読むと、 xxx.js.flow みたいなファイルにInterfaceやClassやらfunctionやらを定義して、exportし、flowconfigの[libs]の欄に xxx.js.flow ファイルがあるディレクトリを追加してあげるとflowコマンド実行時にexportされた定義を見てくれるようになります。

https://flowtype.org/docs/declarations.html

試しにやってみます。ファイル構成はこんな感じです。

$tree
.
├── LICENSE
├── Makefile
├── README.md
├── index.js
├── package.json
├── src
│   ├── flow
│   │   └── hoge.js.flow
│   └── hoge.js
└── test
    └── hoge.test.js

3 directories, 8 files

$cat src/flow/hoge.js.flow
// @flow
export interface Hoge {
    xxx(): number;
}

$cat src/hoge.js
/* @flow */
class HogeImpl {
    a: number;
    constructor(prop: number) {
        this.a = prop;
    }

    xxx(): number {
        return this.a;
    }
}

module.exports = HogeImpl;

$cat index.js
/* @flow */
const HogeImpl = require("./src/hoge");
const h:Hoge = new HogeImpl(1);

console.log(h);

$flow version
Flow, a static type checker for JavaScript, version 0.31.0

$flow
No errors!

index.jsでhという変数に代入されるオブジェクトはHogeというInterfaceを実装していると書いています。
そんな状態でflowを実行したらNo errorsと出ました。
xxxという引数なしのnumber型を返すメソッドをHogeImplに実装しているので当然エラーはないですね。
では、xxxの返す型をstringとかに変えてみましょう。

src/hoge.js

/* @flow */
class HogeImpl {
    a: number;
    constructor(prop: number) {
        this.a = prop;
    }

    xxx(): string {
        return "aaa";
    }
}

module.exports = HogeImpl;

この状態でflowを実行すると下記のようにエラーが出ます。
Interfaceがちゃんと実装されていなければエラーが出てくれるので便利そうです。

$flow
src/hoge.js:8
  8:     xxx(): string {
                ^^^^^^ string. This type is incompatible with
  3:     xxx(): number;
                ^^^^^^ number. See lib: src/flow/hoge.js.flow:3


Found 1 error

babelでコンパイルするときにflow-typeの型を消す

flow-typeの型があると当然javascriptとして実行できません。
なのでnpm publishするタイミングとかに消したいです。
そこで babel-plugin-transform-flow-strip-types を利用します。
これを使えば下記のように型が消えたjavascriptを生成できます。

https://www.npmjs.com/package/babel-plugin-transform-flow-strip-types

# ついでに `babel-preset-es2015` も使ってes5に変換してます。
$babel --plugins transform-flow-strip-types --out-dir lib --presets es2015 src
src/hoge.js -> lib/hoge.js

$cat lib/hoge.js
"use strict";

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var HogeImpl = function () {
    function HogeImpl(prop) {
        _classCallCheck(this, HogeImpl);

        this.a = prop;
    }

    _createClass(HogeImpl, [{
        key: "xxx",
        value: function xxx() {
            return "aaa";
        }
    }]);

    return HogeImpl;
}();

module.exports = HogeImpl;

tips

babelのversionが5.xの時は下記のようなエラーがでます。
このpluginは6.x以降用みたいですね。

$babel --plugins transform-flow-strip-types --out-dir lib --presets es2015 src
TypeError: The plugin "transform-flow-strip-types" didn't export a Plugin instance
    at PluginManager.validate (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/transformation/file/plugin-manager.js:164:13)
    at PluginManager.add (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/transformation/file/plugin-manager.js:213:10)
    at File.buildTransformers (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/transformation/file/index.js:237:21)
    at new File (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/transformation/file/index.js:139:10)
    at Pipeline.transform (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/transformation/pipeline.js:164:16)
    at Object.exports.transform (/usr/local/lib/node_modules/babel/lib/babel/util.js:40:22)
    at Object.exports.compile (/usr/local/lib/node_modules/babel/lib/babel/util.js:49:20)
    at write (/usr/local/lib/node_modules/babel/lib/babel/dir.js:19:21)
    at handleFile (/usr/local/lib/node_modules/babel/lib/babel/dir.js:40:7)
    at /usr/local/lib/node_modules/babel/lib/babel/dir.js:56:9

$babel --version
5.8.23 (babel-core 5.8.25)

mochaでテストを回すときにflow-typeの型がついていても動くようにする

次はテストです。
flow-typeがついたままテスト回せると便利ですよね。
babelでコンパイル後にテスト回したほうがいいのではとか思いつつ。
既にbabelでflow-typeを解釈する設定を行っているので、やることは簡単で、espower-babelを入れれば完了です。
(と思ったらespower-babel deprecatedだったので後で変えます・・・

https://github.com/power-assert-js/espower-babel

// ついでにpower-assertも使ってます
// https://www.npmjs.com/package/power-assert

const Hoge = require("../src/hoge");
const assert = require("power-assert");

describe("Hoge", () => {
    it("should be ...", () => {
        const a = new Hoge(1);
        assert.equal(a.a, 1);
    });
});
$mocha version
3.0.2

$mocha --compilers js:espower-babel/guess --recursive ./test/


  Hoge
    ✓ should be ...


  1 passing (8ms)

src/hoge.js にflow-typeが書いてあってもmochaが実行出来ました。
便利。

まとめ

  • flow-type後々からでも追加できそうなので便利そう
  • babelのpluginもあるし、mochaでも使えて楽
  • ちょっと使ってみよう。