エクスポートしたい(export / import)
1// src/sheets.ts
2export function highlightRow(
3 sheet: GoogleAppsScript.Spreadsheet.Sheet,
4 rowIndex: number,
5 color: string,
6): void {
7 sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn()).setBackground(color);
8}
exportで、モジュールから関数やクラスを外部に公開できます。
これはtsc + rollup + claspのワークフローで
ローカル開発する場合の標準的な書き方で、TypeScript・JavaScriptの言語機能そのものです。
注釈
rollupがexport/importを解決してすべてのモジュールを1つのファイルにバンドルするため、
GAS本体がexport/importに対応している必要はありません。
バンドル後のコードにはexport文自体は残りません。
GASエディターに直接コードを貼り付けて使う場合は事情が異なります。 その場合の書き方は、後述の「GASエディターに直接書きたい場合は」を参照してください。
まとめてエクスポートしたい(export *)
1// src/index.ts(エントリーポイント)
2export * from "./sheets";
プロジェクトルートにエントリーポイントを作成し、
外向けに公開するAPIをまとめてエクスポートできます。
エントリーポイントにはsrc/index.tsのようなファイル名を使うことが多いです。
注意
export * from "./sheets.ts"のように拡張子(.ts)を含めて書くと、
tsconfig.jsonの設定によってはAn import path can only end with a '.ts' extension when...
というエラーになります。
拡張子なし("./sheets")で書くのが基本です。
インポートしたい(import)
1// src/main.ts
2import { highlightRow } from "./sheets";
3// もしくは、エントリーポイント経由でまとめて
4import { highlightRow } from "./index";
importで、他のモジュールがexportした関数やクラスを取り込めます。
rollupがバンドル時にこれらを解決するので、
実行時にrequireのような読み込み処理が発生するわけではありません。
GASエディターに直接書きたい場合は
ここまでのexport/importは、ローカルでTypeScriptを書いてrollupでバンドルする前提の書き方です。
GASエディターで直接.gs(.js)ファイルを編集するスタイルでは事情が異なります。
GASのスクリプトファイルには、標準のexport/importもCommonJSのrequireもありません。
ファイル同士は単に同じグローバルスコープを共有しているだけです。
1// Node環境用のエクスポート(GAS環境では無視される)
2if (typeof module !== 'undefined' && module.exports) {
3 module.exports = {
4 ModuleName,
5 anotherModuleName,
6 someFunction,
7 };
8}
9// GAS環境用のエクスポート(Node環境では無視される)
10else {
11 this.ModuleName = ModuleName;
12 this.anotherModuleName = anotherModuleName;
13 this.someFunction = someFunction;
14}
module.exportsの有無を確認することで、
Node環境(jestなどでのテスト実行時)かGAS環境かを区別し、
両方の環境で動くコードを書く、という古典的なテクニックです。
rollupを使わずに、ローカルでNode向けにテストしつつ、
同じファイルをそのままGASエディターにも貼り付けたい場合に使われます。
1function importClassName() {
2 // ClassName が定義されている場合
3 if (typeof ClassName !== 'undefined') {
4 return new ClassName();
5 }
6
7 // ライブラリで定義されている場合
8 if (typeof LibraryName !== 'undefined' && LibraryName.ClassName) {
9 return new LibraryName.ClassName();
10 }
11
12 // requireが使える場合(=Node環境)
13 if (typeof require === 'function') {
14 try {
15 const { ClassName } = require('./ModuleName');
16 return new ClassName();
17 } catch (e) {
18 throw new Error(`ClassName not available via require()`);
19 }
20 }
21 throw new Error(`ClassName is not available in this environment`);
22}
23
24const _cn = importClassName();
25_cn.someFunction();
インポート側も同様に、typeofでグローバルに定義されているか、
requireが使える環境かを実行時に確認してから読み込みます。
ヒント
tsc + rollup + claspのワークフローが組める場合は、
このセクションの書き方をあえて使う必要はありません。
標準のexport/importのほうがシンプルで、型チェックの恩恵も受けられます。