こんにちは。 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.
xx
とxxx
という変数は定義されていないため、エラーになりました。
コードを実行せずとも、問題を見つけることができました。
型注釈も一切つけていません。
素晴らしいですね。
型が間違ってしまっている場合
少し修正して数値を渡すようにしてみます。
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件エラーがでています。
しかし、xx
とxxx
を定義したので、エラーの内容が変わっています。
1つ目のエラーは「Property token
is incompatible」となっています。
token
に互換性のない型の値が渡されているということですが、xxx
はnumber
でstring
が期待されているところにnumber
が渡されているのでエラーになっています。
2つ目も同様にbody
にnumber
が渡されているのが原因でエラーになっています。
実行する前に問題を見つけることができました。 型注釈も一切つけていません。
素晴らしいですね。(2回目)
エラーがない場合
エラーを修正してみましょう。
// @flow function sendMessage() { var xxx = '', xx = ''; var client = ChatWorkClient.factory({token: xxx}); client.sendMessage({room_id: xx, body: xx}); }
xxx
とxx
がstring
になるようにしました。
実行してみましょう。
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-gas
はGoogle 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
c-note.chatwork.com