詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

Google Apps Script の TypeScript 型定義ファイルを作った

2015-12-08 追記

TypeScript の型定義ファイルリポジトリのデファクト・スタンダードであるところの DefinitelyTyped に無事 Pull Request が取り込まれました ので、今後は DefinitelyTyped のほうを参照していただければと思います!


Google Apps Script は JavaScript っぽい言語で Google 製品の自動化を行える便利環境で、定期実行や外部との HTTP 通信など、意外と痒いところに手が届く出来であり、今となっては身につけておくとよいツールのひとつと言えるでしょう。GAS の開発はおもにオンラインエディタでおこなうこととなっていて、ここで便利なのは補完が効くこと。慣れたエディタで書くこともできるけれど、補完のない環境で、多岐にわたる API をリファレンス引きながら書くというのは心細い。

そもそも JS を書くなら静的型の恩恵のある TypeScript で書きたいという人類原初からの欲求もある。TypeScript は非 TS なライブラリや環境むけに型付きの API 宣言ファイルを用意することで、静的型チェックや補完に利用できる仕組みがある(.d.ts という名前でこれらを用意する)。それならば、GAS の API の型定義ファイルを用意してしまえば、好きなエディタで GAS の補完をしながら書けるはずだと考えるのは、当然の帰結でしょう。

というわけで作ったのがこちら。

motemen/dts-google-apps-script · GitHub

使い方

入手と利用

Spreadsheet や Drive など製品ごと(正確にはリファレンスのカテゴリごと)に定義ファイルを分けているので、必要な物を参照するようにしてください。

けっこうな数のファイルがありますが、主だったところは以下でしょう。定義されるグローバル変数を併記してあります。

  • google-apps-script.base.d.ts -- Logger とか
  • google-apps-script.calendar.d.ts -- CalendarApp
  • google-apps-script.document.d.ts -- DocumentApp
  • google-apps-script.drive.d.ts -- DriveApp
  • google-apps-script.gmail.d.ts -- GmailApp
  • google-apps-script.spreadsheet.d.ts -- SpreadseetApp
  • google-apps-script.url-fetch.d.ts -- UrlFetch

とりあえず DefinitelyTyped には入っていない(テストとか用意したら Pull Request 作ります……)ので、motemen/dts-google-apps-script から入手する必要があります。これには vvakame/dtsm解説)を使うのがオススメです。たとえば Spreadsheet および UrlFetch を使いたい場合は、dtsm.json に以下のように書けばよいです。

  "dependencies": {
      "google-apps-script/google-apps-script.spreadsheet.d.ts": {
          "repo": "https://github.com/motemen/dts-google-apps-script"
      },
      "google-apps-script/google-apps-script.url-fetch.d.ts": {
          "repo": "https://github.com/motemen/dts-google-apps-script"
      }
  }

生成

イケイケドンドンで作ったのではっきり言って読みにくいコードですが、https://developers.google.com/apps-script/ にアクセスして必要な情報を取り出した JSON を生成し(spider.js)、それを d.ts に変換する(gen.js)という 2 パスの工程になっています。Byte や Integer などプリミティブっぽい型に関しては、google-apps-script.types.d.ts というファイルだけ手で用意していて、とりあえずコンパイルが通るようにはなっているのだけれど、これであっているのかはあまり分かっていない。

けっこうページがあるのでスロットリングしつつ並列アクセスしつつやるのは面倒だなと思っていたけれど、ES6 Generators を使ってみたら案外すんなり書けた。co を使って、こんな感じに書くと、MIN_WAIT ミリ秒ごとに CONCURRENCY ページずつアクセスするようにできた。GET したページのスクレイピング自体は cheerio を使いました。

function* visit (url) { ... }

co(function* () {
  ...
  while (Scraper.queue.length > 0) {
    var urls = Scraper.queue.splice(0, CONCURRENCY);
    yield Promise.all(
      urls.map((url) => co(visit(url)))
        .concat(new Promise((ok) => setTimeout(ok, MIN_WAIT)))
    );
  }
});

以上

作ってみたはいいものの、どのくらい使い物になるのかはよくわかってません。試してみてくださいね!

TypeScriptリファレンス Ver.1.0対応

TypeScriptリファレンス Ver.1.0対応

はてなで一緒に働きませんか?