如何將 TypeScript 導入現有 React 專案

Harry Chiu
AsiaYo Engineering
Published in
11 min readJan 4, 2023

前言

TypeScript 是近幾年非常熱門的解決方案,除了具備 JavaScript 本身的所有功能以外,還提供了強大的型態系統(Type System),讓開發者在開發時提前預知可能發生的錯誤,大大減少 Bug 發生的機會這也是為何有越來越多開發者願意將 TypeScript 導入到 JavaScript 專案的原因之一。

近期公司內部開發了一套共用元件庫,這麼做的好處是,設計團隊只需要設計一套共用元件,前端團隊就能應用在多個不同的專案上,大大降低了維護的成本。

關於共用元件庫的開發過程,可以參考 — 透過 babel 製作自己的第三方套件

既然同時有多個專案使用同一個元件庫,元件庫的穩定性就重要許多,我們應該極力避免元件庫發生錯誤,才能確保每個專案都是相對穩定的。

因此,我們決定導入 TypeScript 到元件庫中,利用它嚴謹的型態系統,在編譯階段提前發現大部分錯誤,減少元件庫運行時發生錯誤的機會,從而提升元件庫及其他專案的穩定性。

接下來,這篇文章會分享如何將 TypeScript 和 Babel 這兩個強大的工具做整合,在擁有 Babel 優秀效能的同時,又能擁有 TypeScript 的型態檢查。

為什麼要使用 TypeScript?

導入 TypeScript 勢必會有一定的學習成本,是什麼樣的原因讓我們決定要採用這樣的解決方案呢?主要是考量了 TypeScript 以下的優點:

減少運行時發生的錯誤

在開發 JavaScript 的時候,我們應該對這樣的錯誤訊息都不陌生:

  • TypeError: xxx is not a function
  • TypeError: Cannot read property 'xxx' of undefined

原因大致上都是沒有對變數做好處理,導致某些情境使用這些變數會發生錯誤。而 TypeScript 的型態檢查就能做到在開發時,就立刻告訴開發者,現在使用的寫法可能會發生錯誤,這讓開發者可以在運行前將錯誤修正。

舉例來說,在沒有 TypeScript 的情況下,我們如果在使用 Input 的時候,不小心輸入了錯的參數,或是不存在的參數,並不會看到任何提示:

labels 並不是 Input 的參數,但 IDE 並沒有提示

現在我們將 Input.jsx 改寫成 TypeScript,並看看新的結果:

IDE 會提示錯誤 compile 時找到的錯誤
TypeScript 貼心的告知你使用了不存在的參數

現在 IDE 會告訴我們,labels 並不存在於 Input 的 props 中,也就是說,我們能在程式開跑之前就知道問題出在哪裡,完美的迴避了可能發生的錯誤。

提升開發的便利性

TypeScript 除了型態檢查的功能外,更誘人的是會讓我們的 IDE 能夠使用「自動補完」、「查看型態宣告文件」等功能,讓開發者有更快的開發速度和更高的便利性。

而元件庫這樣的專案,因為要提供給其他專案做使用,「自動補完」、「型態宣告文件」這樣的功能就非常適合它,開發者可以透過 IDE 提供的功能就瞭解元件庫的元件有什麼參數,而不用去看 source code 推導,我認為對開發者來說是非常便利的一件事。

TypeScript 在 IDE 普遍支援自動補完功能

為何不使用 PropTypes 就好

React 的開發者應該對 PropTypes 不陌生,它的存在類似於 TypeScript 的型態系統,都是讓使用者可以對元件的參數做型態定義,主要差別在於:

  • TypeScript 在 compile 時找出錯誤,可以及時發現錯誤。
  • PropTypes 在 runtime 時找出錯誤,沒辦法及時發現錯誤。

除此之外,TypeScript 也提供更多的型態類別讓我們做應用,與 PropTypes 相較起來更加靈活,易讀性也更高。因此,最終我們決定採用 TypeScript。

Babel 和 TypeScript 的差異

在開始整合之前,我們先來瞭解這兩個工具的主要差異。

若想深入了解這兩個工具,可以前往 BabelTypeScript 的官方網站。

Babel

Babel 是一個強大的 JavaScript 編譯器,你可以用最新、最潮的語法進行開發,最後再透過 Babel 將程式碼轉換成瀏覽器能讀懂的舊語法。

主要功能:

  • 語法轉換(ES2015 以上的最新語法、JSX、React 等)
  • 提供 Polyfill

TypeScript

TypeScript 是一個基於 JavaScript 打造的強型態程式語言,讓你在使用 JavaScript 語法的同時,也能享受到強型態所帶來的滿滿安全感。

主要功能:

  • 型態系統
  • 語法轉換(透過 TypeScript 提供的 tsc 編譯器)
  • 型態宣告文件(讓 IDE 能提供自動、跳轉到宣告文件等功能)
Photo by John Schnobrich on Unsplash

整合 Babel 和 TypeScript

有了兩種工具的基本概念後,會發現 Babel 和 TypeScript 都擁有語法轉換的功能,我們有兩個選擇:

  • 捨棄 Babel,TypeScript 處理語法轉換、型態檢查和型態宣告文件
  • Babel 處理語法轉換,TypeScript 處理型態檢查和型態宣告文件

由於 TypeScript 的 tsc 編譯器除了語法轉換外,還需要處理型態檢查、型態宣告文件的工作,導致編譯速度比 Babel 慢上許多。

因此,我們選擇第二個選項 — 「Babel 處理語法轉換,TypeScript 型態檢查和型態宣告文件」,同時這也是官方推薦的整合方式

如此一來,我們既能擁有 Babel 超快的編譯速度,也可以擁有 TypeScript 所提供的型態相關的強大功能!

如果你的需求是第一個選項,可以參考官方提供的教學

整合前的專案架構

我們目前的專案架構如下表所示:

- ui-library
- lib // Babel 輸出路徑
- src // 存放開發檔案
- babel.config.js // Babel 設定
- package.json // 專案設定

接下來會在 ui-library 底下新增 tsconfig.json 的 TypeScript 設定檔,但在那之前,我們先來安裝 Babel 需要的 TypeScript 插件。

安裝 Babel 支援的 TypeScript 插件

話不多說,馬上來安裝 Babel 提供的 @babel/preset-typescript,它讓 Babel 能在編譯時處理 TypeScript 的語法:

完成安裝後,在你的 babel.config.jsbabelrc.json 中設定它:

安裝 TypeScript

完成了 Babel 的設定後,我們要在專案裡安裝 TypeScript,它能讓我們使用 tsc 編譯器來進行型態檢查,及產生型態宣告文件:

TypeScript 安裝完成後,就可以在 ui-library 新增一個 tsconfig.json 的設定檔,開始進行 tsc 的設定:

這個步驟最重要的是開啟 declarationemitDeclarationOnly,確保 tsc 只輸出型態宣告文件,而不輸出 .js 的檔案(交由 Babel 處理),最後透過 outDir 指定將檔案輸出到 Babel 輸出的位置 lib

除了上述的設定以外,tsc 還提供了非常多的客製化設定,大家可以根據自己的專案調整適合的設定,詳細內容請參考 tsconfig 官方文件

撰寫 script

我們的專案已經完成了 Babel 和 TypeScript 的相關設定,現在已經能達到我們所預期的三個目的:

  • 使用 TypeScript 進行型態檢查(IDE,例如:VS Code
  • 使用 TypeScript 輸出型態宣告文件(tsc)
  • 使用 Babel 編譯 .js.ts 檔案

因為 IDE 會自動幫我們進行型態檢查,所以我們只需要將「輸出型態宣告文件」和「Babel 編譯」整合在一起,以後我們就能下一個指令完成這兩件事了。接下來我們就透過 package.jsonscripts 來完成它吧:

來講解一下這三行指令:

  • build:types: 使用 tsc 進行編譯,將型態宣告文件輸出至 lib
  • build:js:使用 babel 編譯 src 內的內容,將結果輸出至 lib,而後方的 --extensions是為了讓 @babel/cli@babel/core 能處理 .js.jsx.ts.tsx 的檔案。
  • build:為上述兩個指令的結合,先執行 build:types 後,再接著執行 build:js

到這邊,我們就完成了 Babel 和 TypeScript 的整合,現在只要執行 yarn build,TypeScript 就會輸出型態宣告文件,Babel 再把程式碼輸出成瀏覽器能看懂的 JavaScript,完美!

有些人可能會好奇,為什麼還需要編譯 .js 或是 .jsx 檔案,是因為我們希望採取漸進式的更新,而不是一口氣整個專案都翻成 TypeScript,這樣更新的成本會比較小。

開始撰寫 TypeScript

現在專案已經具備了 TypeScript 的功能,也能透過 Babel 編譯成瀏覽器看得懂的語法,我們就馬上來為元件庫裡面的 Input 換上 TypeScript 吧!

首先來看一下最原始的 Input.jsx 檔案:

現在我們把 Input.jsx 改造成 Input.tsx

完成之後,執行我們的 yarn build來進行編譯:

可以看到, .ts 已經被成功的編譯成 .js.d.ts 型態宣告文件也和 Input 放在一起,這樣就大功告成啦~

總結

利用 TypeScript 型態檢查的功能,我們能在開發元件庫的當下,就發現可能造成錯誤的根源,例如:參數輸入錯誤、忘記把某項參數傳進元件使用等,常見的人為錯誤都能被 TypeScript 檢查出來,並讓我們能夠提前修正。

以往使用 TypeScript 進行編譯時,容易遇到編譯速度較慢的問題,現在多虧了 Babel 和 TypeScript 共同合作推出的 @babel/preset-typescript 插件,讓我們能將使用 Babel 來編譯 TypeScript,同時又保有 TypeScript 的其他功能,真的是一大福音。

希望這篇文章能幫助到需要導入 TypeScript 到現有專案中的開發者們,若有任何問題和建議,歡迎在下方留言讓我知道。

Thanks for Watching🍻

--

--