Lambda(Node.js)にFlowによる静的型チェックを導入してみる

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

動的型付けを行うJavaScriptにおいて型チェックを行うFlowというライブラリがあります。これをLambdaにも適用できるかを試してみました。

Flowの基本的な使い方については以下の公式サイトのチュートリアルを行うと分かりやすいかと思います。
Getting started with Flow
Five simple examples

手順について

ではFlowを使いLambda Functionを作成してみます。公式サイトとは異なり、今回は全てのモジュールをプロジェクトのローカルフォルダにインストールすることにします。

基本的な考え方としては

  • 型付きのソースを作成してFlowによってチェックする。このソースはsrcフォルダに作成する。
  • babelによる(後述します)自動変換にて、型を取り除いたJavaScriptソースをbuildフォルダに作成する。
  • ローカルやLambda Function上での実行は型を取り除いたJavaScriptソースで行う。

とします。

事前準備

今回の作業を行うフォルダで以下のコマンドを実行します。

$ npm install --save-dev flow-bin
$ npm install babel-cli
$ npm install babel-plugin-transform-flow-strip-types
$ echo '{"plugins": ["transform-flow-strip-types"]}' > .babelrc
$ babel --watch=./src --out-dir=./build

最初のコマンドでFlow自体を、2・3行目以降でbabelとその関連するものをインストールします。最後にbabelを実行して型付きソースの作成時に、自動的に型を取り除いたJavaScriptソースを出力するようにしています。babelについて知りたい場合は、以下のサイトを見て下さい。
Running Flow code

別のターミナルを開き、Flowを初期化します。

$ ./node_modules/flow-bin/flow-osx-v0.32.0/flow init

型チェックと実行

型付きのソースを作成し、実行してみます。型付きのソースはsrcフォルダ内に以下のように作成しました。

src/index.js
/* @flow */

function foo(x: string, y: number): number {
  return x.length * y;
}

exports.handler = function(event: any, context: any) {
  console.log('start.')
  var result: number = foo("Hello", 42);
  console.log('result = ' + result);
};

var event = {};
var context = {};

exports.handler(event, context);

詳細な解説は避けますが、メソッドの引数や変数に型情報が付与されていることが分かるかと思います。このファイルを保存すると、先に起動したbabelによってbuildフォルダ内に型を取り除いたJavaScriptソースが出来ている筈です。

build/index.js
function foo(x, y) {
  return x.length * y;
}

exports.handler = function (event, context) {
  console.log('start.');
  var result = foo("Hello", 42);
  console.log('result = ' + result);
};

var event = {};
var context = {};

exports.handler(event, context);

Flowによるチェックとindex.jsの実行をしてみます。2つのコマンドを連結して実行します。1つ目のコマンドがFlowによるチェック、2つ目のコマンドがindex.jsの実行です。実行するindex.jsはbuildフォルダ内のものであることに注意してください。

$ ./node_modules/flow-bin/flow-osx-v0.32.0/flow; node build/index.js;

No errors!
start.
result = 210

「No errors!」がFlowによるチェック、それ以降がindex.jsの実行結果です。

次に型のエラーを起こしてみましょう。fooメソッドの結果を格納する型を文字列型にしてみます。

(中略)
exports.handler = function(event: any, context: any) {
  console.log('start.')
  var result: string = foo("Hello", 42);
  console.log('result = ' + result);
};
(中略)
$ ./node_modules/flow-bin/flow-osx-v0.32.0/flow; node build/index.js;

src/index.js:9
  9:   var result: string = foo("Hello", 42);
                            ^^^^^^^^^^^^^^^^ number. This type is incompatible with
  9:   var result: string = foo("Hello", 42);
                   ^^^^^^ string


Found 1 error
start.
result = 210

「Found 1 error」と出ています。また実行しているのはbuildフォルダ内の型情報がないindex.jsのため、実行結果は出力されています。

Lambdaへのアップロード

このプログラムをLambda Functionとして設定してみます。このフォルダ毎、以下のコマンドで圧縮します。

$ zip -r upload.zip *

後はいつものLambda Funtionの作成と同じく、圧縮したファイルをアップロードします。注意点としてはLambdaのHandlerは型情報を取り除いたindex.js内にあるので、Handlerは「build/index.handler」とbuildフォルダ内のindex.jsを指定することです。

まとめ

簡単な例ですがNode.jsによるLambda Functionの作成時に型チェックを行うことができました。何かの役に立てば幸いです。