kubell Creator's Note

ビジネスチャット「Chatwork」のエンジニアのブログです。

ビジネスチャット「Chatwork」のエンジニアのブログです。

読者になる

Flowを使ってGoogle Apps Scriptのコードをチェックしよう

こんにちは。 Creator’s Noteには、初登場になるプロダクト開発部の火村(ひむら) a.k.a @eielh です。

弊社では業務効率化やBOT作成のためにGoogle Apps Scriptが使われています。 私はまったく携わっていないので、実はよく知りません。 最近までGoogle Apps Script自体も使ったことありませんでした。 社内でよく使われているものを知らないのはもったいないです。 プライベートでも使い道がたくさんありそうです。 そんなわけで勉強してみることにしました。

しかし、普通に始めるのも面白くありません。 せっかくなので、Flowを導入しながら、始めることにしました。 ということで、今回はFlowを使ってGoogle Apps Scriptのコードをチェックする方法を紹介します。

Flowとは

Facebookオープンソースで、JavaScriptの静的型チェックができるツールです。 Facebookオープンソースはライセンスが話題になっていましたが、めでたく(?)MITライセンスになりました。*1

型注釈や型定義を書くことでチェックをしていくことになるため、TypeScriptと比較されることが多いです。

FlowにはComment Typesという機能があり、コードコメント上に型を記述することができます。 そのため、JavaScriptへの変換をしなくても、そのままで実行可能です。*2 しかし、Google Apps ScriptはJavaScriptをベースにしていますが、完全なJavaScriptであるかは明記されていません。 つまり、TypeScriptを使ってGoogle Apps Scriptを書いていると、トランスパイル後のコードがGoogle Apps Scriptで動くとは限りません。

Google Apps Scriptで利用する場合は、TypeScriptではなく、Flowを使うのであれば不要なトラブルを避けることができます。

チャットワークAPIのクライアントライブラリを利用した例

まずは利用例を紹介します。

Google Apps Scriptとチャットワークといえば弊社の渋谷が投稿した「チャットワークAPI を Google Apps Script で使ってみた」という記事があります。 この記事に登場するサンプルコードをチェックしてみましょう。 (サンプルの実行方法は後で説明します)

上記の記事に登場する「チャットワークにメッセージを送信するコード」をFlowでチェックしてみます。 チェックするのは以下のコードです。

function sendMessage() {
  var client = ChatWorkClient.factory({token: xxx});
  client.sendMessage({room_id: xx, body: xx});
}

まず、Flowを利用するための修正をします。

// @flow
function sendMessage() {
  var client = ChatWorkClient.factory({token: xxx});
  client.sendMessage({room_id: xx, body: xx});
}

変更はなんと1行目の// @flowの部分だけです。

コードチェックをすると以下の結果になります。

Error: index.js:3
  3:   var client = ChatWorkClient.factory({token: xxx}); // typo token
                                                   ^^^ identifier `xxx`. Could not resolve name

Error: index.js:4
  4:   client.sendMessage({room_id: xx, body: xx});
                                    ^^ identifier `xx`. Could not resolve name


Found 2 errors
error Command failed with exit code 2.

xxxxxという変数は定義されていないため、エラーになりました。 コードを実行せずとも、問題を見つけることができました。 型注釈も一切つけていません。

素晴らしいですね。

型が間違ってしまっている場合

少し修正して数値を渡すようにしてみます。

function sendMessage() {
  var xxx = 1, xx = 2;
  var client = ChatWorkClient.factory({token: xxx});
  client.sendMessage({room_id: xx, body: xx});
}

チェックを実行してみます。

Error: index.js:5
  5:   var client = ChatWorkClient.factory({token: xxx}); // typo token
                                           ^^^^^^^^^^^^ object literal. This type is incompatible with the expected param type of
  8:   factory(config: gas$ChatWork$Config): gas$ChatWorkClient$ChatWork;
                       ^^^^^^^^^^^^^^^^^^^ gas$ChatWork$Config. See lib: node_modules/flow-interfaces-chatwork-client-gas/definitions/ChatWork.js:8
  Property `token` is incompatible:
      5:   var client = ChatWorkClient.factory({token: xxx}); // typo token
                                                       ^^^ number. This type is incompatible with
      4:   +token: string;
                   ^^^^^^ string. See lib: node_modules/flow-interfaces-chatwork-client-gas/definitions/ChatWork.js:4

Error: index.js:6
  6:   client.sendMessage({room_id: xx, body: xx});
                          ^^^^^^^^^^^^^^^^^^^^^^^ object literal. This type is incompatible with the expected param type of
 28:   sendMessage(params: gas$ChatWorkClient$SendMessageRequest): gas$ChatWorkClient$Response;
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ gas$ChatWorkClient$SendMessageRequest. See lib: node_modules/flow-interfaces-chatwork-client-gas/definitions/ChatWork.js:28
  Property `body` is incompatible:
      6:   client.sendMessage({room_id: xx, body: xx});
                                                  ^^ number. This type is incompatible with
     12:   +body: string;
                  ^^^^^^ string. See lib: node_modules/flow-interfaces-chatwork-client-gas/definitions/ChatWork.js:12
Found 2 errors

変わらず2件エラーがでています。 しかし、xxxxxを定義したので、エラーの内容が変わっています。

1つ目のエラーは「Property token is incompatible」となっています。 tokenに互換性のない型の値が渡されているということですが、xxxnumberstringが期待されているところにnumberが渡されているのでエラーになっています。 2つ目も同様にbodynumberが渡されているのが原因でエラーになっています。

実行する前に問題を見つけることができました。 型注釈も一切つけていません。

素晴らしいですね。(2回目)

エラーがない場合

エラーを修正してみましょう。

// @flow
function sendMessage() {
  var xxx = '', xx = '';
  var client = ChatWorkClient.factory({token: xxx});
  client.sendMessage({room_id: xx, body: xx});
}

xxxxxstringになるようにしました。 実行してみましょう。

No errors!

このコードを実際に動かしてみてもチャットワークには投稿されないでしょう。静的チェックの限界です。 とても残念です。

プロパティ名を間違えていた場合

人間が書いているので間違えてしまうことがあります。メソッドに渡すオブジェクトのプロパティ名を間違えているとどうなるでしょうか。

// @flow
function sendMessage() {
  var xxx = '', xx = '';
  var client = ChatWorkClient.factory({tokken: xxx}); // typo token
  client.sendMessage({room_id: xx, body: xx});
}

tokenでなければいけないところがtokkenになってます。 私は仕事ではあまり特権は欲しくないです。 それはさておき実行してみましょう。

Library type error:
node_modules/flow-interfaces-chatwork-client-gas/definitions/ChatWork.js:8
  8:   factory(config: gas$ChatWork$Config): gas$ChatWorkClient$ChatWork;
                       ^^^^^^^^^^^^^^^^^^^ property `token` of gas$ChatWork$Config. Property not found in
  4:   var client = ChatWorkClient.factory({tokken: xxx}); // typo token
                                           ^^^^^^^^^^^^^ object literal. See: index.js:4

tokenがみつからないよー」となります。

実行する前に問題を見つけることができました。 型注釈も一切つけていません。

素晴らしいですね。(3回目)

設定と使い方

紹介したサンプルを実行してみたい場合

ちょっと試したい人のためにサンプルプロジェクト(GitHub)を用意しています。 Yarnがインストール済みであれば、yarn && yarn flowで実行できます。(fishをお使いなら yarn; and yarn flow) ちなみに、このサンプルプロジェクトはGoogle Apps Scriptの型定義ファイルは追加していません。

設定方法

Google Apps ScriptとチャットワークAPIの型定義ファイルを利用する場合の設定方法を紹介します。 Yarnの詳細は省きますが、Yarnをお使いの場合は以下のコマンドで設定ができます。

yarn init
yarn add -D flow-interface-google-apps-script flow-interfaces-chatwork-client-gas
cat <<EOF > .flowconfig
[libs]
node_modules/flow-interfaces-google-apps-script/definitions
node_modules/flow-interfaces-chatwork-client-gas/definitions
EOF

あとはコードを書いて、yarn flowを実行するだけで静的型チェックをおこなうことができます。 静的型チェックに問題がなければ、書いたコードをコピー&ペーストやnode-google-apps-scriptを利用してアップロードを行い利用できます。

いうまでもないかもしれませんがflow-interface-google-apps-scriptにはGoogle Apps Scriptの型定義が含まれていて、flow-interfaces-chatwork-client-gasGoogle Apps Scriptで利用できるチャットワークAPIライブラリの型定義が含まれています。

もしモダンなGoogle Apps Scriptの開発環境をお探しの場合はぜひ試してみてください。

補足ですが、Google Apps Script用の型定義ファイルはまだすべて定義されていないようなので、ご注意ください。

まとめ

Flowを使うと、既存のコードにコメントを追加するだけで型チェックができます。 利用するライブラリの型定義があれば、自分の書いたコードに型を明示しなくてもある程度チェックできます。 実行する前に実行時のエラーを防ぐことができるようになるので、効率的にプログラミングができます。

Flow以外にも静的型チェックをする方法はありますが、Google Apps ScriptがどこまでブラウザのJavaScriptと同じ動きをするかわかりません。 Babelを通したり、TypeScriptのコンパイル後のコードが正しく動くかわかりません。 その点、FlowのComment TypesはBabelを通す必要がないので安心です。

今回長くなったため省きましたが、Flowを使うことでエディタでのコード補完も強化されます。

Google Apps Scriptの型定義ファイルはこの記事を書いている時点ではすべてを網羅できていませんが、日本人が作成しているので、気軽にプルリクエストしてみてはどうでしょうか。

関連リンク

flow.org www.npmjs.com www.npmjs.com

http://c-note.chatwork.com/post/69590585422/chatworkapi-gas-library
c-note.chatwork.com

*1:この記事を書いている時点ではウェブサイトに記載されたライセンスはまだ修正されていませんでしたが、GitHubのmasterブランチはMITになってました。

*2:コメントで記述するのは少し冗長です。Babelを利用することでトランスパイルすることもできます