[譯] TypeScript 和 Babel:美麗的結合

zsky發表於2019-03-18

Babel and TypeScript

由於 TypeScript 和 Babel 團隊官方合作了一年的專案:TypeScript plugin for Babel@babel/preset-typescript),TypeScript 的使用變得比以往任何時候都容易。這篇文章會告訴你為何 TypeScript 和 Babel 是完美配對的 4 點原因,並會教你在 10 分鐘內一步步地升級到 TypeScript。

哈?什麼?為什麼?

我一開始並不理解這個 preset 的必要性。

Babel 和 TypeScript 不是兩個完全不一樣的東西麼?Babel 能怎麼處理 TypeScript 的型別檢查?TypeScript 早已能像 Babel 一樣輸出 ES5 程式碼,這有什麼意義呢?把 Babel 和 TypeScript 合併起來不是會把事情複雜化麼?

在幾個小時的調研後,我的結論是:

TypeScript 和 Babel 是美麗的結合。

讓我來告訴你原因。

1)你早已使用 Babel(或者應該如此)

你屬於這三個類別之一:

  1. 你早已使用 Babel。如果不是直接使用,你的 Webpack 配置會把 *.js 檔案提供給 Babel(大多數腳手架都是這種情況,包括 create-react-app
  2. 使用了 TypeScript,卻沒有用 Babel。針對這種情況,你可以考慮把 Babel 加入你的武器庫中,因為它提供了許多獨特的功能。繼續閱讀,你會了解更多!
  3. 你不使用 Babel?是時候開始用了。

編寫現代 JavaScript 而不破壞任何東西

你的 JavaScript 程式碼需要在舊瀏覽器中執行?沒問題,Babel 會轉換程式碼,而且不會出現任何問題。使用最新和最好的功能,無需擔心。

TypeScript 編譯器具有類似的功能,可通過將 target 設定為 ES5ES6 來實現。但 Babel 配置通過 babel-preset-env 改進了這方面功能。你可以列出需要支援的環境,而不是鎖定一組特定的 JavaScript 功能(ES5,ES6 等):

"targets": {
  "browsers": ["last 2 versions", "safari >= 7"],
  "node": "6.10"
}
複製程式碼

Babel 使用 compat-table 來檢查要轉換的 JavaScript 功能以及針對這些特定目標環境做 polyfill。

compat-table

花點時間欣賞那個將這個專案命名為 ‘compat-table’ 的天才。

create-react-app 使用了一種有趣的技術:在開發期間以最新的瀏覽器進行編譯(為了速度),並在生產中以更大範圍的瀏覽器進行編譯(為了相容性)。漂亮。

Babel 是超級可配置的

想要 JSX? Flow?TypeScript?只需安裝一個外掛,Babel 就可以處理它。有大量的 官方外掛,主要涵蓋即將推出的 JavaScript 語法。 還有很多第三方外掛:improve lodash importsenhance console.log,或 strip console.log。在 awesome-babel 列表中找到更多資訊。

不過要小心。如果外掛顯著改變了語法,那麼 TypeScript 可能無法解析它。例如,備受期待的 optional chaining proposal 提議有一個 Babel 外掛:

Optional chaining

@babel/plugin-proposal-optional-chaining

但不幸的是,TypeScript 無法理解這種更新的語法。

不要緊張,有另一種選擇...

Babel 巨集

你知道 Kent C Dodds 嗎?他創造了一個改變遊戲規則的 Babel 外掛:babel-plugin-macros

你可以將巨集安裝為依賴項並將其匯入程式碼中,而不是將外掛新增到 Babel 配置檔案中。當 Babel 正在編譯時,巨集會啟動,並根據需要修改程式碼。

來看一個例子。 它使用 idx.macro 來解決痛點,直到 optional chaining proposal 提議通過:

import idx from 'idx.macro';

const friends = idx(
  props,
  _ => _.user.friends[0].friends
);
複製程式碼

編譯為:

const friends =
  props.user == null ? props.user :
  props.user.friends == null ? props.user.friends :
  props.user.friends[0] == null ? props.user.friends[0] :
  props.user.friends[0].friends
複製程式碼

巨集是相當新的,但很快就越來越受歡迎。特別是整合在 create-react-app v2.0 後。 CSS in JS 被覆蓋:styled-jsxstyled-componentsemotion。Webpack 外掛在被移植中:raw-loaderurl-loaderfilesize-loader。還有更多列在 awesome-babel-macros

這是最好的部分:與 Babel 外掛不同,所有 Babel 巨集都與 TypeScript 相容。它們還可以幫助減少執行時依賴,避免一些客戶端計算,並在構建時提前捕獲錯誤。 檢視 此帖子 瞭解更多詳情。

Improved console.log

更好的 console.log:scope.macro

2)管理一個編譯器更容易

TypeScript 需要它自己的編譯器 — 它提供了驚人的型別檢查超能力。

令人沮喪的日子(在 Babel 7 之前)

將兩個獨立的編譯器(TypeScript 和 Babel)連結在一起並非易事。編譯流程變為:TS > TS Compiler > JS > Babel > JS (again)

Webpack 經常用於解決這個問題。調整 Webpack 配置以將 *.ts 提供給 TypeScript,然後將結果提供給 Babel。但是你使用哪種 TypeScript loader?兩個流行的選擇是 ts-loaderawesome-typescript-loaderawesome-typescript-loaderREADME.md 提到它對一些工作負載來說可能更慢,並建議使用 ts-loader 加上 HappyPackthread-loaderts-loaderREADME.md 推薦結合 fork-ts-checker-webpack-pluginHappyPackthread-loader,和(或)cache-loader

啊。不。這是大多數人不堪重負的地方,並把 TypeScript 放在“太難”的籃子裡。我不怪他們。

One does not simply configure TypeScript

陽光燦爛的日子(有了 Babel 7)

擁有一個 JavaScript 編譯器不是很好嗎?無論你的程式碼是否具有 ES2015、JSX、TypeScript 或瘋狂的自定義功能 - 編譯器都知道該怎麼做。

我只是描述了 Babel。厚臉皮了。

通過允許 Babel 充當單個編譯器,不需要使用一些複雜的 Webpack 魔術來管理,配置或合併兩個編譯器。

它還簡化了整個 JavaScript 生態系統。他們只需要支援 Babel,而不是支援不同編譯器的語法檢查、測試執行器、構建系統和腳手架。然後,配置 Babel 以滿足你的特定需求。告別 ts-nodets-jestts-karmacreate-react-app-typescript 等,並使用 Babel 支援代替。對 Babel 的支援無處不在,請檢視 Babel 設定 頁面:

Babel and TypeScript

Babel 覆蓋你的需求。

3)編譯速度更快

警告!有一個震驚的訊息,你可能想坐下來好好聽下。

Babel 如何處理 TypeScript 程式碼?它刪除它

是的,它刪除了所有 TypeScript,將其轉換為“常規的” JavaScript,並繼續以它自己的方式愉快處理。

這聽起來很荒謬,但這種方法有兩個很大的優勢。

第一個優勢:️⚡️閃電般快速⚡️。

大多數 Typescript 開發人員在開發/監視模式下經歷過編譯時間長的問題。你正在編寫程式碼,儲存一個檔案,然後...它來了...再然後...最後,你看到了你的變更。哎呀,錯了一個字,修復,儲存,然後...啊。它只是慢得令人煩惱並打消你的勢頭。

很難去指責 TypeScript 編譯器,它在做很多工作。它在掃描那些包括 node_modules 在內的型別定義檔案(*.d.ts),並確保你的程式碼正確使用。這就是為什麼許多人將 Typescript 型別檢查分到一個單獨的程式。然而,Babel + TypeScript 組合仍然提供更快的編譯,這要歸功於 Babel 的高階快取和單檔案發射架構。

因此,如果 Babel 剝離掉 TypeScript 程式碼,那麼編寫 TypeScript 有什麼意義呢?這帶來了第二個優勢...

4)只有在準備好後才檢查型別錯誤

你為了快速做出一個解決方案來看看你的想法是否有根據,會把一些程式碼改到一起。當你按下儲存按鈕的時候,TypeScript 向你尖叫:

“不!我不會編譯這個!你的程式碼在 42 個不同的檔案中有問題!”

是的,你知道它已經不能執行了。你可能也破壞了一些單元測試。但是你只是在這一點上進行實驗。要在所有時間持續確保所有程式碼是型別安全的,這一點讓人火大。

這是 Babel 在編譯期間剝離 TypeScript 程式碼的第二個優點。你編寫程式碼,儲存,並且編譯(非常快)而不檢查型別安全性。繼續嘗試解決方案,直到你準備好檢查程式碼是否有錯誤。這種工作流程可讓你在編碼時保持專注。

那麼如何檢查型別錯誤?新增一個呼叫 TypeScript 編譯器的 npm run check-types 指令碼。我將我的 npm test 命令調整為首先檢查型別,然後繼續執行單元測試。

這不是完美的結合

根據 公告文章,有四種 TypeScript 功能由於其單檔案發射架構而無法在 Babel 中編譯。

別擔心,它沒有那麼糟糕。當啟用 isolatedModules 標誌時,TypeScript 將對這些問題發出警告。

1)名稱空間。

解決方案:不要使用它們!他們已經過時了。請改用行業標準 ES6 模組(import / export)。推薦 tslint 規則 確保名稱空間被使用。

2)使用<newtype>x 語法轉換型別。

解決方案:使用 x as newtype

3)Const 列舉

這很羞愧。現在需要使用常規列舉。

4)傳統風格的 import / export 語法。

示例:import foo = require(...)export = foo

在我多年 TypeScript 的使用中,我從未遇到過這種情況。誰這樣編碼?停下來!

好的,我準備嘗試使用 Babel 和 TypeScript!

Yeah!

rawpixel.com 拍攝

我們開工吧!它應該只需要大約 10 分鐘。

我假設你設定了 Babel 7。如果沒有,請參閱 Babel 遷移指南

1)將 .js 檔案重新命名為 .ts

假設你的檔案位於 /src 中:

find src -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} ;
複製程式碼

2)將 TypeScript 新增到 Babel。

安裝一些依賴:

npm install --save-dev @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
複製程式碼

在你的 Babel 配置檔案裡(.babelrcbabel.config.js)新增:

{
  "presets": [
    "@babel/typescript"
  ],
  "plugins": [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread"
  ]
}
複製程式碼

TypeScript 有一些 Babel 需要了解的額外功能(通過上面列出的兩個外掛)。

Babel 預設查詢 .js 檔案,遺憾的是,這在 Babel 配置檔案中是不可配置的。

如果你使用 Babel CLI,新增 --extensions '.ts'

如果你使用 Webpack,新增 'ts'resolve.extensions 陣列。

3)新增 “check-types” 命令。

package.json 裡新增:

"scripts": {
  "check-types": "tsc"
}
複製程式碼

這個命令只是簡單地呼叫 TypeScript 編譯器(tsc)。

tsc 來自哪裡?我們需要安裝 TypeScript:

npm install --save-dev typescript
複製程式碼

為了配置 TypeScript(和 tsc),我們需要在根目錄下有 tsconfig.json 檔案:

{
  "compilerOptions": {
    // Target latest version of ECMAScript.
    "target": "esnext",
    // Search under node_modules for non-relative imports.
    "moduleResolution": "node",
    // Process & infer types from .js files.
    "allowJs": true,
    // Don't emit; allow Babel to transform files.
    "noEmit": true,
    // Enable strictest settings like strictNullChecks & noImplicitAny.
    "strict": true,
    // Disallow features that require cross-file information for emit.
    "isolatedModules": true,
    // Import non-ES modules as default imports.
    "esModuleInterop": true
  },
  "include": [
    "src"
  ]
}
複製程式碼

完成。

好了,設定完成了。現在執行 npm run check-types(監聽模式:npm run check-types -- --watch)並確保 TypeScript 對你的程式碼滿意。你可能會發現一些你不知道但卻存在的錯誤。這是件好事!這份 從 Javascript 遷移 指南也會給你一些幫助。

Microsoft 的 TypeScript-Babel-Starter 指南包含其他設定說明,包括從頭開始安裝 Babel,生成型別定義(d.ts)檔案,以及將其與 React 一起使用。

關於語法檢查呢?

使用 tslint

更新(2019 年 2 月):使用 ESlint!自1月份以來,TypeScript 團隊一直 專注於 ESLint 整合。由於 @typesript-eslint 專案,很容易配置 ESLint。如需參考,請檢視我的 超級 ESLint 配置,其中包括 TypeScript、Airbnb、Prettier 和 React。

Babel + TypeScript = 美麗的結合

Love hearts

Akshar Dave 拍攝

Babel 是你需要的唯一一個 JavaScript 編譯器。它可以被配置去處理任何事情。

沒有必要與兩個相互競爭的 JavaScript 編譯器鬥爭。簡化你的專案配置,並充分利用 Babel 與語法檢查、測試執行器、構建系統和腳手架的驚人整合。

Babel 和 TypeScript 組合可以快速編譯,並允許你專注地編碼,只有在你準備好時才檢查型別。

預測:TypeScript 使用將會上升

根據最新的 Stack Overflow 開發者調查,JavaScript 是最流行的語言,TypeScript 排在第 12 位。 對於TypeScript 來說,這仍然是一項偉大的成就,擊敗了 Ruby、Swift 和 Go。

Developer survey results

我預測 TypeScript 將在明年進入前 10 名。

TypeScript 團隊正在努力推廣。這個 Babel preset 是為期一年的合作,他們的新焦點是在 改進 ESLint 整合。這是一個聰明的舉措 — 利用現有工具的功能、社群和外掛。開發有競爭力的編譯器和語法檢查是浪費精力。

通往 TypeScript 的路徑已經被鋪平了,我們只需調整我們喜愛的工具配置即可。進入的障礙已被打破。

隨著 VS Code 的普及,開發人員已經設定了一個很棒的 TypeScript 環境。寫程式碼時的自動補全將帶來歡樂的淚水。

它現在也整合到 create-react-app v2.0 中,將 TypeScript 展現給每月有 20 萬次下載的使用者。

如果你因為設定太難而推遲使用 TypeScript,這不再是一個藉口。是時候試一試了。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章