[譯] TypeScript 和 Babel:一場美麗的婚姻

原文地址:TypeScript With Babel: A Beautiful Marriage 做者: Matt Turnbull, Feb 12,2019javascript

感謝 Babel 的 TypeScript 插件@babel/preset-typescript), TypeScript 從未變得如此簡單,這是 TypeScript 和 Babel 團隊長達一年的官方合做成果。本文列舉出了4條理由來證實 TypeScript 和 Babel 是完美的一對,以及10分鐘內升級到 TypeScript 的步驟指南。

哈?什麼?爲何?

我一開始並不理解這個新 preset 要解決的需求。html

Babel 和 TypeScript 難道不是2種徹底不一樣的東西嗎?Babel 是如何處理 TypeScript 的類型檢查的?TypeScript 已經能夠像 Babel 那樣輸出 ES5 了,因此目的是啥?合併 Babe 和 TypeScript 會不會讓事情變得更復雜?java

通過 1 個小時的研究,個人結論是: TypeScript 和 Babel 的結合是一場美麗的婚姻。node

Let me show you.react

1)已經使用了 Babel(或者應該使用)

您必定屬於如下三類狀況之一:webpack

  1. 已經在使用 Babel,若是不是直接地使用,Webpack 也會將 *.js 文件提供給 Babel (不少框架模板就屬於這類狀況,包括 creat-react-app)。
  2. 使用 TypeScript 但不使用 Babel。請考慮向項目的工具庫中添加 Babel,它會帶來不少獨一無二的功能,請繼續讀下去。
  3. 壓根不使用 Babel? 那麼如今是時候跳上 Babel 這條船了。

在不會跳出任何錯誤的狀況下編寫現代 JavaScript

須要在老舊瀏覽器中運行 JavaScript 代碼?沒問題,Babel 會將代碼轉換,而且搞定全部問題。直接使用最新和最棒的特性,並且不用擔憂任何事情。git

TypeScript 的編譯器也有類似的功能,是經過設置 target 的值爲 ES5ES6 來達到。可是 Babel 利用 babel-preset-env 來改善這個功能,而不是鎖定一組特定的 JavaScript 功能(ES5,ES6 等等),只須要列出所須要支持的環境便可:github

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

Babel 使用 compat-table 來檢查須要轉換的 JavaScript 特性,以及指定的目標環境所須要的 polyfill。web

creat-react-app 使用過的一種有意思的技術:在開發過程當中,按照最新的瀏覽器進行編譯(爲了加快編譯速度),而在產品發佈階段按照多種瀏覽器進行編譯(爲了兼容性),Nice~

Babel 是超級可配置的

想要 JSX?Flow?TypeScript?只要安裝插件,Babel 就能處理它們了。這裏有至關多的官方插件可供選擇,涵蓋了大多數即將推出的 JavaScript 語法,也有很是多的第三方插件:改進 lodash import增強版 console.log,或者 清理 console.log。能夠在 awesome-babel 的列表中找到更多插件。typescript

可是請當心,若插件明顯地改變了語法,TypeScript 可能會不能解析它。例如,有一個 Babel 插件:@babel/plugin-proposal-optional-chaining,可以實現備受期待的可選鏈提案

const obj = {
    foo: {
        bar: {
            baz: 42
        }
    }
};

const baz = obj?.foo?.bar?.baz; // 42

const safe = obj?.qux?.baz; // undefined
複製代碼

遺憾的是,TypeScript 沒法理解這個即將推出的語法。

但不要有壓力,還有替代方案...

Babel Macros

不知道您是否據說過 Kent C Dodds,他創造了一個改變 Babel 遊戲規則的插件:babel-plugin-macros

它由直接向 Babel config 文件中添加插件,改成把宏指令(macro)做爲依賴安裝而且 import 到代碼中。當 Babel 正在編譯的時候,宏指令開始起做用,而且修改代碼。

舉個例子,在可選鏈特性正式加入到標準以前,咱們能夠先使用 idx.macro 來達到相同的效果。

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
複製代碼

Babel Macro 是至關的新的概念,可是很快受到了熱捧。尤爲是登錄到 create-react-app v2.0,JS 中的 CSS 包括 styled-jsxstyled-components,和 emotion。正在移植 Webpack 插件:raw-loader,url-loader和 filesize-loader。 還有更多列在 awesome-babel-macros 上。

最棒的是,不一樣於 Babel 插件,全部的 Babel Macro 都和 TypeScirpt 兼容。它們仍能夠幫助減小運行時的依賴,避免客戶端計算,而且在構建時提早捕獲異常。查看這篇帖子獲取瞭解詳情。

一個更好的console.log:scope.macro
上圖演示了一個更好的 console.log: scope.macro

2)只管理一個編譯器更輕鬆

TypeScirpt 須要它本身的編譯器——由於它提供了超棒的類型檢查功能。

在灰暗的日子裏(Babel 7以前)

將兩個獨立的編譯器(TypeScript 和 Babel)串聯在一塊兒是可不是一件容易的工做。編譯流程變成:TS > TS 編譯器 > JS > Babel > JS (再次)

Webpack 常常用於解決這個問題,調整 Webpack 的配置。將 *.ts 提供給 TypeScript,而後將運行的結果提供給 Babel。可是用哪一個 TypeScript loader 呢?2個很是流行的選擇是:ts-loaderawesome-typescript-loaderawesome-typescript-loaderREADME.md中提到,在某些工做量中它可能會比較慢,而且建議使用 ts-loader 配合 HappyPack 或者 thread-loaderts-loaderREADME.md文件推薦結合使用 fork-ts-checker-webpack-pluginHappyPackthread-loader 以及(或者)cache-loader

夠了。。這就是吞沒大多數同窗的地方,也是你們還給 TypeScript 貼上「太難」標籤的緣由之一。這不怪你們。

光明降臨的日子(Babel 7)

只有一個 JavaScript 編譯器難道很差嗎?不管代碼是否具備 ES2015 特性,JSX,TypeScript,仍是其餘瘋狂的自定義————編譯器都知道要作什麼。

我剛剛只是在描述Babel。

經過容許 Babel 做爲惟一的編譯器來工做,就再也不必利用一些複雜的 Webpack 魔法來管理、配置或者合併兩個編譯器。

它還精簡了整個 JavaScript 生態系統。取代了 ESLint、測試 runner、build 系統,以及開發模板提供的不一樣的編譯器,它們只須要支持 Babel 便可。而後配置 Babel 來處理具體的需求。向 ts-loader、ts-jest、ts-karma、create-react-app-typescript 等等說再見就好啦,使用 Babel 代替它們。Babel 的支持無處不在,查看 Babel setup 頁面:

3)更快地編譯

Babel 是如何處理 TypeScript 的?它移除了 TypeScript。

是的,它刪除了全部 TypeScript ,將其轉換爲「常規」 JavaScript,並繼續使用它高興的方式。

聽起來挺荒唐的,可是這種實現具備兩個很是強大的優點。

第一個優點:⚡️快如閃電⚡️

大多數的 TypeScript 開發者在 devlement/watch 模式中,會遇到很是緩慢的編譯速度。你如今正在敲代碼,保存文件,接下來是很漫長的等待。。。終於,你看到了你更改的內容。啊哦,不當心寫錯字了,修改,保存,而後。。。夠了!它不只慢得使人煩躁,還打擊了你編程的動力。

咱們也不能去責怪 TypeScript 編譯器,它在作的工做實在太多了。它在掃描類型定義文件(*.d.ts),包括node_modules裏的,以確保你的代碼里正確地使用。這就是爲何不少人將 TypeScript 類型檢查分爲一個獨立的進程。然而,Babel + TypeScript 的組合套餐依舊會提供更快的編譯,這要歸功於 Babel 的優秀的緩存和單文件散發架構。

所以,若是 Babel 剝離了 TypeScript 的代碼,那麼編寫 TypeScript 的意義何在呢?這帶來了第二個優點。。。

4)只在當你準備好的時候檢查類型錯誤

如今你正在開心地編程,不假思索地提出解決方案來驗證你的想法是否奏效。你保存文件,TypeScript 卻對你大喊:

「不!我不會編譯這玩意兒的!你的代碼在42個不一樣的文件中出現異常!」

對,你知道它有異常。可能在幾個單元測試中已經出現異常了。可是你可能只是想在這裏作一個實驗。老是持續不斷地進行類型安全性檢查有時候是挺煩人的。

這就是 Babel 在編譯過程當中剝離 TypeScript 的第二個優點。編寫,保存,而後它會快速編譯(速度很快)而不須要進行類型安全性檢查。這樣就能夠在你準備好檢查代碼錯誤以前,盡情地去對解決方案作實驗。

那如何去檢查類型錯誤呢?添加一段 npm run check-types 腳原本喚起 TypeScript 編譯器。我將個人 npm test 命令調整爲先檢查類型,而後再繼續運行單元測試。

這不是一段完美的婚姻

根據項目公告能夠了解到,因爲 Babel 的單文件發射架構,有四種TypeScript 特性沒法在 Babel 中編譯。

不過不用擔憂,還不算太糟糕。並且當啓用 isolatedModules 的配置選項時,TypeScript 將對這些問題作出警告。

1)Namespace

解決方案:不要用!它們已是過期的了。改用標準的 ES6 module(import/export),在推薦的 tslint 規則中也建議不要使用 namesapce。

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

解決方案:改用 x as newtype

3)const 枚舉

這個鍋沒得甩,目前只能用常規的枚舉,期待將來可以支持。

4)歷史遺留風格的 import/export 語法

好比:import foo = require(...)export = foo

我寫 TypeScript 這麼多年,就歷來沒這麼寫過。誰是這麼寫的?可別再這麼幹了!

OK, 準備好嘗試一下使用 Babel 來寫 TypeScript 了

開始搞起!僅僅須要10分鐘。

假定你已經安裝了 Babel 7,若是沒有,請查看 Babel 遷移指南

1)將 .js 重命名爲 .ts

假設文件存儲在 /src 目錄下:

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

2)向 Babel 添加 TypeScript

安裝幾個依賴:

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 須要瞭解的額外特性(經過上面列出的2個插件)。

Babel 默認查找 .js 文件,遺憾的是,你還沒辦法在 Babel 的 config 文件中進行配置。

若是使用 Babel CLI,添加 --extensions '.ts'

若是使用 Webpack,向 resolve.extensions數組中添加 'ts'

3)添加 'check-type' 命令

package.json 中添加:

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

這條命令只是簡單地喚起 TypeScript 編譯器(tsc)。

經過安裝 TypeScript 來獲取 tsc

npm install --save-dev typescript
複製代碼

在根目錄裏添加 tsconfig.json 文件來配置 TypeScript 和 tsc

{
	"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"
	]
}
複製代碼

完成

OK,配置完成。如今運行 npm run check-types (監聽模式:npm run check-types -- --watch),確保 TypeScript 代碼正常運行。你可能會發現一些未知但確實存在的錯誤,這未必是件壞事,這篇Javascript 遷移指南能夠提供一些幫助。

微軟的 TypeScript-Babel-Starter 包含其餘的設置說明,包括從零安裝 Babel,生成類型定義(d.ts)文件,以及將其與 React 一塊兒使用。

代碼檢查工具咋弄?

使用 tslint

於2019年2月更新:使用 ESLint !TypeScript 團隊自從1月份開始就在專一於 ESLint 集成。歸功於 @typescript-eslint 項目,配置 ESLint 變的很是簡單。如需靈感,請查看個人 終極ESLint配置,其中包括 TypeScript,Airbnb,Prettier和 React。

Babel + TypeScript = 美麗的婚姻

Babel是惟一須要的 JavaScript 編譯器,它能夠經過配置來處理任何事情。

沒有必要讓兩個 JavaScript 編譯器競爭,簡化項目配置,並充分利用 Babel 與 ESLint,單元測試,構建系統和項目模板集成在一塊兒的優點。

Babel 和 TypeScript 的組合能夠快速編譯,並容許在編碼時留在免打擾的空間裏,而且只有在準備好的時候才檢查類型。

預言: TypeScript 將會崛起

根據最新的 Stack Overflow 開發者調查,JavaScript 是最流行的語言,TypeScript 落後於第12名。對於TypeScript來講,這仍然是一項偉大的成就,由於它擊敗了 Ruby,Swift 和 Go。

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

TypeScript 團隊正在努力和其餘團隊合做。這個 Babel preset 是爲期一年的合做成果,他們的新焦點是改進ESLint集成。這是一個聰明的舉措——利用現有工具的功能,社區和插件。開發編譯器和代碼檢查器去和別人競爭簡直是浪費精力。

只需調整咱們喜好的工具的配置便可鋪設進入 TypeScript 的路徑。TypeScript 入口的障礙已被掃清。

隨着 VS Code 的普及,開發人員已經設置了一個很是棒的 TypeScript 環境。

它如今也集成到 create-react-app v2.0 中,將 TypeScript 以每個月20萬次下載量的規模提供給用戶。

若是你遲遲不肯接觸 TypeScript,由於它很難設置,它再也不是一個藉口,如今是時候去試一試了。

相關文章
相關標籤/搜索