有一種說法是 9102 年作 Node 後臺開發還不上 Typescript 不是太懶惰就是太菜了,其實假如你使用 Egg.js, 他們已經對基於 ts 開發作了許多支持,在項目裏引入 ts 並無那麼困難,且對減小開發過程當中的低級錯誤很是有幫助。
在這裏記錄一下我的在 Egg.js 項目裏引入 ts 的一些約定,其實都在官方文檔、文章和 Github issue 裏了。javascript
web 開發不是瀑布流,定了需求閉門開發幾個月上線,每每是基於線上運行的 js 項目,要改形成基於 ts,前提是不能中斷服務,初步設想有如下三種方案:html
第一種方式在定義 .d.ts 文件時很是繁瑣且會有一些坑,如很難推斷函數返回類型,且在使用一些 es 原生方法時覆蓋類型會有坑;前端
第二種方式須要實現一種流量切換的方式,如加入 haproxy 中間層,經過 header 或是 path 等方式來識別新老接口流量。碰到的主要問題是在新老項目一個服務每每要改動多個公共文件,很難保持同步,會有矛盾發生;java
第三種方式是最後採用的方式,從 js 代碼中切出一個新分支,寫了腳本批量替換文件後綴,花了大約 2-3 天完成遷移,期間在舊 js 代碼中添加的新功能,須要確認在新 js 代碼中也實現一遍。
過程當中也是發現了一些坑,舉例:node
entries(o: {}): [string, any][];` 假如傳入的參數類型是某種字符串字面量,這裏變到 s 以後類型就成了 stringgit
參考 該文章github
在本地開發時,使用 egg-bin dev 命令啓動應用,egg-bin 自帶 ts-node,能夠直接運行 ts 代碼,無需編譯過程;web
發佈到線上時,egg 官方建議是仍是使用 Node + js 原生代碼,使用 egg-script start 啓動,因此須要將 ts 代碼編譯成 js 的過程。docker
在後端開發時能夠主動升級 Node 版本,使用一些先進的 es 語法新特性,不像前端爲了編譯成瀏覽器廣泛能兼容的 es5 代碼會使用不少 polyfill 的技巧,因此 ts 代碼編譯成 js 後其實並無太難懂(tsconfig.json 裏 compilerOptions => target -> es2017)。儘管如此使用編譯後的 js 代碼仍是須要映射回 ts 代碼,以便線上報錯時能有比較清晰的提示,ts 採用的方案是 sourceMap (tsconfig.json => compilerOptions =>inlineSourceMap -> true)。
關於 sourceMap 的原理能夠參考阮一峯的這篇文章。typescript
egg-bin 在加載文件時,若是識別到同目錄下的 .ts 文件和 .js 文件,會優先加載 .js 文件。
真實環境下,爲了不把 .ts .js 文件同時託管在 git 上,形成混亂,最好是引入 ci 層完成編譯的工做,這時就可使用單獨的 dist 目錄來託管編譯後的 js 代碼,無需上述 egg-ts-helper 操做,設置 tsconfig.json => compilerOptions => outDirs -> ./dist 便可。
egg 擴展了 koa 的application、context 對象,掛載了 controller、service、model 等對象在上面,然而你本身定義的 controller 文件 ts 沒法感知他們和 egg 的關係,因此 egg 提供了 egg-ts-helper 工具來完成來自動生成 .d.ts 文件,放在 typings 目錄。參考文章
因爲 model 目錄並非 egg 默認約定會使用的目錄,只是大多數開發者的一個通識,因此針對 model 目錄 egg-ts-helper 並不會主動生成 .d.ts 文件,你須要主動提供 tshelper.js 文件,利用 egg-ts-helper 提供的 api 來 watch model 目錄。
啓用 egg-ts-helper 的方式很簡單,老的方式是 egg-bin dev -r egg-ts-helper/register 新的方式是設置 package.json => egg => declarations -> true
線上使用 ci 負責編譯工做,ci 服務器須要下載所有依賴(包括 dependencies 和 devDependencies),獲得編譯產物後,再發布到 staging 服務器,staging 服務器只運行 npm install --production, 打包成 docker 鏡像再發布