終於來到名為「本地開發GAS專案」的舞台劇的最後一幕,主要是因為我有實作的就只到這裡。其實原本想把這部分放到最後一週再聊,畢竟後面幾天我想分享的故事,應該都沒有esbuild和TypeScript的戲份😅
前兩天(Day09&Day10)分享了我個人隨興的esbuild配置,讓我掌握自己合併文檔的主動權,快樂地在GAS專案玩起熟悉的import
/export
遊戲。如果還想要更痛更快樂,我們可以再加上JavaScript社群最陌生的老朋友:TypeScript。
這一小段是寫給來自Python或其他語言背景、因為我個人無法想像的原因而需要寫GAS專案的開發者,JavaScript生態系的讀者可以跳至實作步驟。[^1]
直覺上,就字面上來說,TypeScript就是JavaScript加上型別,有點類似C++之於C。我自己的心智地圖,更偏向把它們放在SCSS之於CSS的關係:
如Day02提到的,撰文的當下,採用ES標準、實作JavaScript的引擎之中,市占率最高的是Google陣營的V8引擎;TypeScript則是微軟陣營為了自家生態系的需求所擴充的一系列額外機制,其中type只是TypeScript這個產品的識別特色,而不僅僅處理type。例如早年async
/await
的語法糖、箭頭函式等等這類現在屬於JavaScript的語法,都是TypeScript率先實作。此外,不同於社群色彩更強的SCSS,TypeScript的很多功能也依賴於微軟生態系的VS Code。就我的觀點,TypeScript不僅僅是語言,更是一套服務。
那麼,是否有必要導入TypeScript?我覺得可以由GAS環境負擔的行政自動化場景都沒必要。就算你是偏好強型別(例如Rust的開發者),也得面對靜態型別的限制與TypeScript各種workaround,它只在編譯前、編譯時檢查,無法在執行時檢查。
使用你偏好的套件管理工具進行安裝,例如pnpm或npm:
pnpm add -D typescript
npm install -D typescript
pnpm add -D @types/google-apps-script
npm install -D @types/google-apps-script
tsconfig.json
[^2]{
"compilerOptions": {
// 只做型別檢查,編譯交給esbuild
// 官方建議若esbuild則搭配ESNext
"noEmit": true,
"module": "ESNext",
// globalThis被正式納入ECMAScript標準是在ES2020
// GAS環境換V8引擎也是在2020年
"target": "ES2020",
"lib": [
"ES2020"
],
// 讓編譯器讀懂GAS環境特有型別
"types": [
"google-apps-script"
],
// 啟用所有嚴格型別檢查選項,包含ES的strict mode與TS的其它檢查
"strict": true
},
"include": [
"src"
]
}
That's all. 把文檔貼上很快,但我每次導入TypeScript都還是設定好久。[^3]相比於esbuild,TypeScript沒什麼需要斟酌的部分,主要就是記得下載由社群整理好的GAS環境的型別。
包含前幾天的配置,對於.ts
文檔的處理流程會是:
.gs
我個人目前會用Node.js腳本,針對正式環境與測試環境分別放進對應的文檔:
fs
.copyFile("config/._appsscript.json", "dist/appsscript.json")
.then(() => console.log("└─dist/appsscript.json")),
fs
.copyFile(`config/_config.${env}.ts`, "src/config/config.ts")
.then(() => console.log("└─src/config/config.ts")),
fs
.copyFile(`config/._clasp-${env}.json`, ".clasp.json")
.then(() => console.log("└─.clasp.json")),
並在package.json
設定指令:[^4]
"scripts": {
"env": "node scripts/switch-env.js",
"build": "node scripts/build.js",
"deploy:stag": "pnpm run env stag && tsc && pnpm run build && clasp push",
"deploy:prod": "pnpm run env prod && tsc && pnpm run build && clasp push"
}
如此一來,就可以使用pnpm run deploy:stag
或pnpm run deploy:prod
跑完上述的處理流程。
如果有讀者竟然還想繼續追加負重的話,可以參考debiruさん所配置的clasp範例庫,他甚至加上了Jest……真的不愧是主張「HTMLには魂が宿っている」的職人。身為沒有寫過單元測試的citizen developer,儘管完全不懂QA流程,但光是想像在GAS環境要用Jest模擬測試環境,那難度肯定遠比esbuild還可怕。[^5]
從上週末(Day07)的後話可以看出來,我原本打算把TypeScript與esbuild的額外配置放到最後一週再來談,畢竟在常見的行政自動化場景,這些額外配置並非必要。
只是我是一邊寫稿一邊瀏覽、回顧自己既有的專案配置,就順著回憶寫完了。我個人實際使用這套配置,目前為止依然覺得TypeScript可以放到最後才導入。[^6]
是說,esbuild是用Go寫的,而半年前TypeScript開始用Go改寫[^7],感覺都是在擺脫Node.js的瓶頸。JavaScript生態系始終圍繞在如何更好地管理、整合library,包含npm這個巨大的資安弱點。從原本以自舉為主的工具鏈[^8],漸漸有轉向Go、Rust等跨語言重寫的趨勢?
[^1]: 在這個後GenAI時代,各位有經驗的開發者們一定可以有效率地獲取各種客觀資訊,這裡索性就放大我的主觀角度。
[^2]: https://www.typescriptlang.org/tsconfig
[^3]: 突然想到前陣子看到一則關於TypeScript的貼文,底下有人留言分享他每個案子有三分之一時間都在寫config。我真的很好奇這是不是JavaScript生態系獨有的特色,其他背景的開發者們有興趣閒聊的話,歡迎DM我。
[^4]: 由於env是一個保留字,通常不推薦用來當指令名,這也是為什麼就算pnpm
相比npm
可以省略run
,通常也不建議省略。
[^5]: 就我個人接觸到的有限資訊,主觀覺得日本社群有好多這種非主流實作嘗試。給人一種不需要急著追逐市場趨勢,不因為沒有商業價值就急著否定的オタク印象。一直以來很好奇到底是什麼支撐了這麼龐大的社群文化。
[^6]: 也正因為這篇文章的實用價值不高,所以假想受眾時,乾脆徹底預設讀者皆為前GenAI時代就有開發經驗的人,寫起來意外地比前面幾篇文章更順暢。說到底這我這系列文章的定位真的很尷尬,到底誰會Python專案或modern web寫著寫著,會突然需要碰GAS專案🫠
[^7]: https://devblogs.microsoft.com/typescript/typescript-native-port/
[^8]: 例如Babel用JavaScript寫JavaScript、Webpack用JavaScript打包JavaScript、TypeScript用TypeScript編譯TypeScript。