グローバル変数したい(globalThis)
1(globalThis as Record<string, unknown>)["myFn1"] = myFn1;
2(globalThis as Record<string, unknown>)["myFn2"] = myFn2;
3(globalThis as Record<string, unknown>)["myFn3"] = myFn3;
4(globalThis as Record<string, unknown>)["myFn4"] = myFn4;
5
6// もしくは
7const g = globalThis as Record<string, unknown>;
8g["myFn1"] = myFn1;
9g["myFn2"] = myFn2;
globalThisは、ECMAScript標準の環境に依存しないグローバル変数の置き場所です。
src/index.tsのようなエントリーポイントとなるファイルで、
ほかのモジュールからexportした関数をglobalThisに登録しておくとよいです。
とくにGASでは、rollupでバンドルすると、関数は内部スコープに閉じ込められてしまいます。
そのままではGASから参照できませんが、globalThisに登録することで、外から呼べるようになります。
注釈
JavaScriptでは、ブラウザ環境だとwindow、Node環境だとglobal、
のように、環境によってグローバル変数名が違います。
globalThisを使えば、環境を気にせずに同じ書き方で利用できます。
型安全に登録したい
1import { myFn1, myFn2, myFn3 } from "./module";
2
3interface GASGlobalThis {
4 myFn1?: typeof myFn1;
5 myFn2?: typeof myFn2;
6 myFn3?: typeof myFn3;
7}
8
9if (typeof globalThis !== "undefined") {
10 const gas = globalThis as unknown as GASGlobalThis;
11 gas.myFn1 = myFn1;
12 gas.myFn2 = myFn2;
13 gas.myFn3 = myFn3;
14}
Record<string, unknown>+文字列キーの書き方は手軽ですが、
キー名をタイプミスしても型チェックでは検出できません。
公開する関数・クラスの一覧をinterfaceとして定義しておくと、
存在しないプロパティへの代入がコンパイルエラーになるため、より安全です。
typeof globalThis !== "undefined"のガードは、
globalThisが存在しない環境(テスト実行時など)でエラーにならないようにするためのものです。
トリガー関数を登録したい
1(globalThis as Record<string, unknown>)["onFormSubmit"] = onFormSubmit;
2(globalThis as Record<string, unknown>)["onEdit"] = onEdit;
3
4// 手動実行する関数は、先頭にアンダースコアを付けて登録する
5(globalThis as Record<string, unknown>)["_setupTriggers"] = setupTriggers;
ScriptApp.newTrigger("関数名")でトリガーを作成する場合、
指定する文字列はglobalThisに登録したキー名と完全に一致している必要があります。
onFormSubmit・onEditのようなイベントハンドラー系の関数は、素の名前のまま登録します。
ヒント
Apps ScriptエディターのRunドロップダウン(実行ボタンの隣の関数選択)には、
トップレベルで宣言された関数しか表示されません。
rollupでバンドルしたcode.jsの中身はすべて内部スコープに隠れてしまうため、
このままではエディターから手動実行できません。
対策として、エディターから直接実行したい関数だけ、別ファイル(例:stubs.js)に
以下のような「橋渡し用」のトップレベル関数を用意しておく方法があります。
1// stubs.js(clasp pushの対象に含める)
2function setupTriggers() {
3 globalThis["_setupTriggers"]();
4}
このとき、バンドル本体(code.js)側では実装をアンダースコア付きの名前
(_setupTriggers)で登録しておく必要があります。
アンダースコアなしで登録してしまうと、GASがトップレベル宣言をglobalThisに
巻き上げる際にstubs.js側の宣言と衝突し、
スタブが自分自身を呼び出して無限ループ(スタックオーバーフロー)になります。