TypeScript | 當 TypeScript 遇上 React 的超級聯名 TSX 款
前言
Hi!大家好,我是神 Q 超人。其實標題應該是反過來了,因為我是先擁有一套完整的 React 專案架構,再硬是將 TypeScript 加進專案中,所以應該是 React 遇到 TypeScript 才對。
本文就是要記錄下過程,一是防範金魚腦的我將來忘記到底做了什麼,二是也希望能給想導入 TypeScript 的 React 工程師一個小參考 🙌 。
最後再提醒一下,文內說明的專案並非是 Create React App 架構,需要的東西全都是自己配置的,所以可能不適用於使用 Create React App 的讀者。
React 專案內容
這裡筆者擁有的 React 專案內容分成以下部分:
- Webpack 、 Babel : Webpack 負責打包所有套件及編譯, Babel 在編譯時負責轉換 ES6 及 JSX 的語法。
- SCSS : CSS 的預處理器,也有 loader 讓 Webpack 編譯。
- ESLint :約束語法規則。
- Jest :負責做單元測試。
這裡也提供初始的專案內容的 GitHub 讓大家 Clone ,他是跑得動的,也包含了上述架構,文章內也會依照此專案為基礎加上 TypeScript ,大家也可以依照需要來選擇修改哪個部分。
前置作業
因為筆者的 TypeScript 是直接裝在 Global 上,但還是記得另外裝在專案中,如果不想放 Global ,想只裝在該專案的 dev 也可以:
npm install typescript --save-dev
安裝完後需要產生 TypeScript 的 config file :
tsc --init
需要注意的是,如果 TypeScript 是安裝在 dev ,就不能直接執行上方的指令,而是要先將指令放入 package.json 的 script 中,再藉由 npm run
執行。
Webpack、Babel
這兩個把他們放在一起講,主要是下載 Babel 提供的 Presets ,讓 Webpack 可以抓到 .tsx
編譯:
npm install @babel/preset-typescript --save-dev
下載後到 webpack.config.js 中,原本的 rule
內有針對 .js
和 .jsx
的 loader
設定,這裡將他們改成 .ts
和 .tsx
,並在 presets
內加上剛剛下載的 @babel/preset-typescript 。
還有一個地方不能忘記,就是預設編譯的檔案在 entry
中是設置為 index.js
,也要改成 index.tsx
:
修改完第一個階段後,便可以將撰寫 Component 的 .js
檔案改成 .tsx
, src目錄會變成:
src/
component/
Main/
index.scss
index.ts
Main.tsx
index.tsx
接著打開負責用 react-dom 將 Component 渲染到 HTML 的檔案(上圖中的 src/index.tsx ):
會發現 import
上紅通通的一片。
先來處理 react 和 react-dom 的部分,首先安裝 @types/react 和 @types/react-dom ,因為專案裡改用了 TypeScript ,所以會檢測到原本的 react 和 react-dom 沒有指定型別,而這兩個套件可以為它們設置靜態型別,處理這個問題:
npm install @types/react @types/react-dom --save-dev
安裝完後可以看到他們變成綠色的了:
接下來是 import
Component 的部分,這裡比較簡單,只需打開 tsconfig.json ,將 jsx
的設定打開:
但是這麼簡單的步驟我卻卡了很久,請大家記得要重開編輯器, TypeScript 才會重新檢查,重開後錯誤就只剩下 ESLint 的了:
ESLint 稍後再來做設定,那接下來可以嘗試用 webpack -p
編譯專案,應該會出現下列的錯誤:
那是因為, Webpack 預設只讀 .js
檔,所以突然冒出個 .ts
他是無法在目錄中找到的,所以打開 webpack.config.js 加上以下設置:
加上去後再執行一次 webpack -p
,就能成功了:
網頁上看起來也沒問題, CSS 也正確吃到:
但是當我們認為一切沒問題的時候,打開 Main.tsx
,卻發現 import
CSS 的地方卻出現錯誤:
SCSS
第二階段,主要做的事是將 index.scss
轉換成 .ts
做匯出,讓其他的 .ts
檔案 import
,因此我們下載 typed-scss-modules ,他能夠替我們做到這件事:
npm install typed-scss-modules --save-dev
然後這裡也是千千萬萬要注意,如果你沒有用任何預處理器,純用 CSS 的話,就下載 typed-css-modules 就好了,否則是不會正確轉換的。
下載完後到 package.json 的 script
加上一行新指令:
tsm
後方的 src
是路徑,剛剛的套件會去找該路徑下所有的 .scss
檔案,將他編譯成能讓 .ts
做 import
的模組,現在我們用 npm run
執行他看看:
如果編譯成功,就會看見該目錄下新增了一個 index.scss.d.ts
的檔案,裡面會幫你將所有 ClassName 做 export
:
然後回到 Main.tsx
,會發現原本在 import
時會出現語法不正確的提示已經消失了:
如果覺得每一次修改都必須要記得編譯 .scss
很麻煩的話,他也有 watch
模式,讓 .scss
有異動時同時編譯,除此之外也可以設定他編譯後的命名方式,例如:camel (駱駝命名法)、 kebab (短橫線命名法)等等,都可以參考 GitHub 上的 README 介紹。
ESLint
這個階段是語法檢查, ESLint 可能有些不適合 .tsx
的檢查,例如剛剛編譯後的 SCSS , ESLint 就搞不懂冒號是在幹嘛:
因此要下載一個適用於 TypeScript 檢查的規則:
npm install eslint-plugin-typescript --save-dev
npm install @typescript-eslint/parser --save-dev
eslint-plugin-typescript
是依賴套件, @typescript-eslint/parser
是主要的 Parser 。
下載完後到 ESLint 的設定檔 .eslintrc.js ,加上以下的設定:
記得要重新打開編輯器,讓 ESLint 重新跑一遍,跑完後就能看到原本提示有問題的地方消失了:
Jest
終於到測試了,但是這裡先緩著點,畢竟我們的 Main
,都還是用純純正正的 .jsx
,只要把副檔名遮住沒人知道他其實是 .tsx
:
因此需要對他進行個大改造,這裡先快速帶過,之後會用其他文章介紹,畢竟重點是在於如何使用 Jest 測試 TypeScript ,所以戴上無限手套的我彈指間,讓 Main
變成這樣:
現在 Main
已經完全 TypeScript 化了,那執行測試會發生什麼事:
這一部份最簡單,因為測試不經過 Webpack 編譯,他是直接吃 .babelrc.js 的設定編譯,所以只需要替他加上剛剛下載的 @babel/preset-typescript 就行了:
加上後再執行一次測試,就能成功囉:
其實整個過程是不難的,但是因為專案內使用了許多不同的套件,所以當時在導入時常常這個過了又出現下一個問題,但幸好 StackOverflow 真的很給力,所以導入時間沒有很長,就可以讓專案順利地跑起來了。
如果文章中有任何問題或是講解不清楚的地方,再麻煩留言告知,我會盡快修改或回覆的,謝謝大家!
參考文章
- https://stackoverflow.com/questions/43595555/webpack-cant-resolve-typescript-modules
- https://medium.com/@shihKai/react-typescript-webpack-%E5%88%9D%E5%BF%83%E8%80%85%E8%BC%95%E9%AC%86%E5%BB%BA%E7%BD%AE%E6%94%BB%E7%95%A5-8caaa32cc474
- https://juejin.im/post/5a7803335188257a5d2b0fed
- https://juejin.im/entry/5a156adaf265da43231aa032