トランスパイルしたい(tsc)

$ npx tsc
$ npx tsc src/main.ts
$ npx tsc --watch

tscコマンドで、TypeScript(.ts)をJavaScript(.js)に変換するコマンドです。 npx経由で実行することが多いです。

設定ファイル(tsconfig.json)がある場合は、自動で読み込まれます。

インストールしたい(typescript)

$ npm install --save-dev typescript @types/google-apps-script

typescriptパッケージをdevDependenciesとして追加します。

@types/google-apps-scriptは、GASの型情報を使うための型定義パッケージです。 明示的なインポートは必要ありません(というか不可です)。

注釈

devDependenciesにある@typesパッケージはVS Codeが自動で認識して、補完/チェックしてくれるようです。

@types/google-apps-scriptの中身は、DriveAppやSpreadsheetAppなどのサービスごとに分かれた.d.tsファイルの集まりです。 それぞれのファイルの中では、GoogleAppsScript.DriveやGoogleAppsScript.Spreadsheetのように、 サービス名の名前空間(namespace)の中にFileやSheetといった型が定義されています。

1// GoogleAppsScript.Drive.File 型が返る
2const files: GoogleAppsScript.Drive.FileIterator = DriveApp.getFiles();
3
4// 変数に型注釈をつけたいときは GoogleAppsScript.<サービス名>.<型名> の形式で書く
5function processFile(file: GoogleAppsScript.Drive.File): void {
6  Logger.log(file.getName());
7}

DriveAppやSpreadsheetAppのようなグローバル変数自体も、 declare var DriveApp: GoogleAppsScript.Drive.DriveApp;のように、 この名前空間の型を指す形でグローバルに宣言されています。 そのためimportしなくても、DriveApp.createFile(...)のように直接呼び出すだけで型補完が効きます。

ヒント

具体的な型名がわからないときは、VS CodeでDriveApp.getFiles()のような呼び出しにカーソルを合わせ、 「定義へ移動」(Go to Definition)を使うと、対応する.d.tsファイルと型名を直接確認できます。

型チェックだけしたい(tsc --noEmit)

$ npx tsc --noEmit

rollupを使う構成では、実際のトランスパイル・バンドルは@rollup/plugin-typescriptが担当するため、 tsc単体でファイルを出力する必要はありません。 --noEmitを付けると、ファイル出力をせずに型チェックだけを実行できます。 rollup実行前のCIやコミット前のチェックとして使うと便利です。

型をつけたい

1// 変数の定義
2const 変数名: 型名 = 値
3
4// 関数の定義
5function 関数名(引数名: 型名): 戻り値の型名 {...}

: 型名で変数や関数の型を指定できます。 この型を使って、トランスパイルや静的解析で潜在的なエラーを検出します。

注釈

トランスパイルしたあとのJavaScriptには型の情報は残りません。

スクリプト設定したい(package.json)

{
    "name": "...",
    "scripts": {
        "build": "tsc --noEmit",
        "bundle": "rollup -c",
        "bundle:watch": "rollup -c --watch",
        "push": "clasp push",
        "pull": "clasp pull",
        "deploy": "npm run build && npm run bundle && npm run push",
        "...": "..."
    }
}

buildで型チェックのみ、bundleで実際のトランスパイル・バンドルを行います。 紛らわしいですが、GASの世界では「ビルド=型チェック」「バンドル=1ファイルへのまとめ」と役割を分けるのが定番のようです。 deployでは、型チェック→バンドル→GASへのアップロードの順に実行しています。

設定したい(tsconfig.json)

{
    "compilerOptions": {
        "target": "ES2020",    // GAS V8対応
        "module": "ESNext",    // rollupでバンドル前提
        "moduleResolution": "bundler",
        "lib": ["ES2020"],
        "rootDir": "src",    // ソースコードのルート
        "outDir": "gas",    // 出力先ディレクトリ(型チェックのみなら未使用)
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,    // 厳格な型チェック
        "skipLibCheck": true    // 依存パッケージの型チェックをスキップ
    },
    "include": [
        // トランスパイル対象
        "src/**/*"
    ],
    "exclude": [
        // トランスパイル対象外
        "node_modules",
        "gas"
    ]
}

tsconfig.jsonでTypeScriptのトランスパイルの設定ができます。 GASのV8ランタイムはECMAScript2020相当の機能までサポートしているため、それに合わせた設定にしています。 ここではrollupでモジュールをバンドルし、 claspでデプロイする前提でサンプルを作成しました。

targetは、トランスパイルして出力されるJavaScriptのECMAScriptバージョンを指定するオプションです。 GAS V8ランタイムでは"ES2020"程度まで指定できます。

moduleは、トランスパイルするときに利用するモジュール形式を指定するオプションです。 rollupなどでバンドルする場合は"ESNext"を指定しておけばよさそうです。

注釈

モダンブラウザ向けにESModuleを使う場合は、"ES2015"や"ES2020"などを指定します。 Node.jsを使う場合は"CommonJS"を指定します。GASでは非対応です。

moduleResolutionは、importやrequireで指定されたモジュールの探し方を指定するオプションです。 rollupなどのバンドラーを使う場合は"bundler"を指定します。

注釈

outDirを省略すると、.jsは各.tsファイルと同じ場所に出力されます。 慣習的にoutDirにはdistがよく使われますが、 本ページではclaspの.clasp.jsonを置くディレクトリと合わせる意図で、あえてgasという名前にしています。

rollupでバンドルする構成ではtsc自体はファイルを出力しない(前述のtsc --noEmit)ので、 outDirはほとんど意味を持ちません。

skipLibCheckは、node_modulesにある型定義ファイル(.d.ts)の型チェックを省略するオプションです。 @types/google-apps-scriptなど、サードパーティの型定義に問題があっても自分のコードのチェックを止めないために有効にしておくと安心です。