clasp + TypeScriptで作成したときの変換の挙動をメモする
zennに書こうと思ったけど、ほとんど自明なことなのでブログにメモとして残します。
残課題はarrayの関数の変換
claspとは
googleが作成しているGASをローカル上で開発できるようにしているツールです。 - https://github.com/google/clasp
TypeScriptにも対応しているため、型の恩恵等を受けることができます。 - https://github.com/google/clasp/blob/master/docs/typescript.md - 内部的にはts2gasを利用しているそうです。 - https://github.com/grant/ts2gas
初期設定
まずはプロジェクトの初期設定をします。 プロジェクトを作成して、型定義のnpmを追加します。 clasp系の設定はリンク等を参照してください。 - https://qiita.com/HeRo/items/4e65dcc82783b2766c03
npm init -y mkdir src npm i -S @types/google-apps-script clasp create --type standalone --rootDir ./src
- 公式のドキュメントにしたがって、tsconfig.jsonも設定します。
{ "compilerOptions": { "lib": ["esnext"], "experimentalDecorators": true } }
- 最初の挙動を確かめるために、サンプルにあったソースをそのまま利用します。
const greeter = (person: string) => { return `Hello, ${person}!`; } function testGreeter() { const user = 'Grant'; Logger.log(greeter(user)); }
- GASにプッシュします。
clasp push
- GASのコンソールから結果を確認してみます。
- constがvarになっているのと文字の連結が変わっています。
// Compiled using clasp-test 1.0.0 (TypeScript 4.3.5) var greeter = function (person) { return "Hello, " + person + "!"; }; function testGreeter() { var user = 'Grant'; Logger.log(greeter(user)); }
各種挙動を確認する
- class等の記法がどのような形で変換されるのかを見ていきます。
class
- 変換前
class Clazz { constructor(private member: number) {} get item() { return this.getItem(); } getMember() { return this.getMember(); } private getItem() { return 'item'; } static factory(member: number) { return new Clazz(member); } }
- 変換後。prototypeベースに置き換わっています。
// Compiled using clasp-test 1.0.0 (TypeScript 4.3.5) var Clazz = /** @class */ (function () { function Clazz(member) { this.member = member; } Object.defineProperty(Clazz.prototype, "item", { get: function () { return this.getItem(); }, enumerable: false, configurable: true }); Clazz.prototype.getMember = function () { return this.getMember(); }; Clazz.prototype.getItem = function () { return 'item'; }; Clazz.factory = function (member) { return new Clazz(member); }; return Clazz; }());
import/export
変換前
- module.ts
export const hoge = () => 'hoge'; export const constTest = 'test'
- sample.ts
import { hoge, constTest } from "./module"; function test() { console.log(hoge()); console.log(constTest); }
変換後
- module.gs
// Compiled using clasp-test 1.0.0 (TypeScript 4.3.5) var exports = exports || {}; var module = module || { exports: exports }; exports.constTest = exports.hoge = void 0; var hoge = function () { return 'hoge'; }; exports.hoge = hoge; exports.constTest = 'test';
- sample.gs
// Compiled using clasp-test 1.0.0 (TypeScript 4.3.5) var exports = exports || {}; var module = module || { exports: exports }; //import { constTest, hoge } from "./module"; hoge();
- sample.gsは実行すると失敗します
ReferenceError: constTest is not defined
- よくよく見てみると、export const で定義したfunctionは成功しますが、変数はエラーになっています。
- これは、module.tsでhogeはvarとして定義されているために、global変数だとみなされているため参照できるようになっています。
ただし、constTestはexportsのメンバ変数として定義されているので、exports.constTestのように呼び出ししないとうまく動作しません
- にしても、exportsがグローバルなので、変数の競合があると意図しない動きになると思います。
解消方法としては以下の3点があります。ここでは詳細では取り上げません。興味があれば公式のドキュメントを見てみてください。
- exportsを明示的に変数として定義して、そこから読み込むようにする
- なんかトリッキーな書き方ですね。複数のモジュールをimportしたいときにうまく動かなそうな感じがする。
- namespaceを利用する
- namespace使っていいのか問題はあるが、割と簡単。
- 先にwebpack等でトランスパイルしてからGASにプッシュする
- GAS上で見にくくなってもいいならよさそう
- exportsを明示的に変数として定義して、そこから読み込むようにする
namespace
変換前
namespace Module { export function foo() {} function bar() {} // this function can only be addressed from within the `Module` namespace }
変換後
// Compiled using clasp-test 1.0.0 (TypeScript 4.3.5) var Module; (function (Module) { function foo() { } Module.foo = foo; function bar() { } // this function can only be addressed from within the `Module` namespace })(Module || (Module = {}));
- Moduleという変数が定義されていて、それの変数として、foo、barが定義されています。
- そのため、moduleは外からの呼び出しにも対応できます。
array
変換前
const number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; number.find((i) => i === 10); number.findIndex((i) => i === 10); number.filter((i) => i % 2); number.map((i) => i ^ i); number.reduce((pre, cur) => pre + cur, 0); number.some((i) => i % 2); number.slice(0); [...number].sort((a, b) => b - a); number.forEach(a => a); number.every(i => true);
変換後
// Compiled using clasp-test 1.0.0 (TypeScript 4.3.5) var __spreadArray = (this && this.__spreadArray) || function (to, from) { for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) to[j] = from[i]; return to; }; var number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; number.find(function (i) { return i === 10; }); number.findIndex(function (i) { return i === 10; }); number.filter(function (i) { return i % 2; }); number.map(function (i) { return i ^ i; }); number.reduce(function (pre, cur) { return pre + cur; }, 0); number.some(function (i) { return i % 2; }); number.slice(0); __spreadArray([], number).sort(function (a, b) { return b - a; }); number.forEach(function (a) { return a; }); number.every(function (i) { return true; });
- スプレッド変数のみ変換がかかっていて、ほかのarrayはそのままになっています。
- ES3相当の変換なのに、ES3で存在しないはずの変数が残っている。。ここではまったので別の機会に解消の仕方を書きます。