最近的一段時間一直在搞TypeScript
,一個巨硬出品、賦予JavaScript
語言靜態類型和編譯的語言。
第一個徹底使用TypeScript
重構的純Node.js
項目已經上線並穩定運行了。
第二個先後端的項目目前也在重構中,關於前端基於webpack
的TypeScript
套路以前也有提到過:TypeScript在react項目中的實踐。html
可是這些作完之後也總感受缺了點兒什麼 (沒有盡興):前端
是的,依然有五分之一的JavaScript
代碼存在於項目中,做爲一個TypeScript
的示例項目,表現的很不純粹。
因此有沒有可能將這些JavaScript
代碼也換成TypeScript
呢?
答案確定是有的,首先須要分析這些代碼都是什麼:node
Webpack
打包時的配置文件- 一些簡單的測試用例(使用的mocha和chai)
知道了是哪些地方還在使用JavaScript
,這件事兒就變得很好解決了,從構建工具(Webpack
)開始,逐個擊破,將這些所有替換爲TypeScript
。react
Webpack 的 TypeScript 實現版本
在這8102
年,很幸福,Webpack
官方已經支持了TypeScript
編寫配置文件,文檔地址。
除了TypeScript
之外還支持JSX
和CoffeeScript
的解釋器,在這就忽略它們的存在了webpack
依賴的安裝
首先是要安裝TypeScript
相關的一套各類依賴,包括解釋器及該語言的核心模塊:git
npm install -D typescript ts-node
typescript
爲這個語言的核心模塊,ts-node
用於直接執行.ts
文件,而不須要像tsc
那樣會編譯輸出.js
文件。github
ts-node helloworld.ts
由於要在TypeScript
環境下使用Webpack
相關的東東,因此要安裝對應的types
。
也就是Webpack
所對應的那些*.d.ts
,用來告訴TypeScript
這是個什麼對象,提供什麼方法。web
npm i -D @types/webpack
一些經常使用的pLugin
都會有對應的@types
文件,能夠簡單的經過npm info @types/XXX
來檢查是否存在typescript
若是是一些小衆的plugin
,則可能須要本身建立對應的d.ts
文件,例如咱們一直在用的qiniu-webpack-plugin
,這個就沒有對應的@types
包的,因此就本身建立一個空文件來告訴TypeScript
這是個啥:npm
declare module 'qiniu-webpack-plugin' // 就一個簡單的定義便可 // 若是還有其餘的包,直接放到同一個文件就好了 // 文件名也沒有要求,保證是 d.ts 結尾便可
放置的位置沒有什麼限制,隨便丟,通常建議放到types
文件夾下
最後就是.ts
文件在執行時的一些配置文件設置。
用來執行Webpack
的.ts
文件對tsconfig.json
有一些小小的要求。
compilerOptions
下的target
選項必須是es5
,這個表明着輸出的格式。
以及module
要求選擇commonjs
。
{ "compilerOptions": { "module": "commonjs", "target": "es5", "esModuleInterop": true } }
但通常來說,執行Webpack
的同級目錄都已經存在了tsconfig.json
,用於實際的前端代碼編譯,極可能兩個配置文件的參數並不同。
若是由於要使用Webpack
去修改真正的代碼配置參數確定是不可取的。
因此咱們就會用到這麼一個包,用來改變ts-node
執行時所依賴的配置文件:tsconfig-paths
在Readme
中發現了這樣的說法:If process.env.TS_NODE_PROJECT is set it will be used to resolved tsconfig.json
。
在Webpack
的文檔中一樣也提到了這句,因此這是一個兼容的方法,在命令運行時指定一個路徑,在不影響原有配置的狀況下建立一個供Webpack
打包時使用的配置。
- 將上述的配置文件更名爲其它名稱,
Webpack
文檔示例中爲tsconfig-for-webpack-config.json
,這裏就直接沿用了 - 而後添加
npm script
以下
{ "scripts": { "build": "TS_NODE_PROJECT=tsconfig-for-webpack-config.json webpack --config configs.ts" } }
文件的編寫
關於配置文件,從JavaScript
切換到TypeScript
實際上並不會有太大的改動,由於Webpack
的配置文件大多都是寫死的文本/常量。
不少類型都是自動生成的,基本能夠不用手動指定,一個簡單的示例:
import { Configuration } from 'webpack' const config: Configuration = { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', } export default config
Configuration
是一個Webpack
定義的接口(interface
),用來規範一個對象的行爲。
在VS Code
下按住Command
+ 單擊能夠直接跳轉到具體的webpack.d.ts
定義文件那裏,能夠看到詳細的定義信息。
各類經常使用的規則都寫在了這裏,使用TypeScript
的一個好處就是,當要實現一個功能時你再也不須要去網站上查詢應該要配置什麼,能夠直接翻看d.ts
的定義。
若是註釋寫得足夠完善,基本能夠當成文檔來用了,並且在VS Code
編輯器中還有動態的提示,以及一些錯誤的糾正,好比上述的NODE_ENV
的獲取,若是直接寫process.env.NODE_ENV || 'development'
是會拋出一個異常的,由於從d.ts
中能夠看到,關於mode
只有三個有效值production
、developemnt
和none
,而process.env.NODE_ENV
顯然只是一個字符串類型的變量。
因此咱們須要使用三元運算符保證傳入的參數必定是咱們想要的。
以及在編寫的過程當中,若是有一些自定義的plugin
之類的,可能在使用的過程當中會拋異常提示說某個對象不是有效的Plugin
對象,一個很簡單的方法,在對應的plugin
後邊添加一個as webpack.Plugin
便可。
在這裏TypeScript
所作的只是靜態的檢查,並不會對實際的代碼執行形成任何影響,就算類型由於強行as
而改變,也只是編譯期的修改,在實際執行的JavaScript
代碼中仍是弱類型的
在完成了上述的操做後,再執行npm run XXX
就能夠直接運行TypeScript
版本的Webpack
配置咯。
探索期間的一件趣事
由於個人項目根目錄已經安裝了ts-node
,而前端項目是做爲其中的一個文件夾存在的,因此就沒有再次進行安裝。
這就帶來了一個使人吐血的問題。
首先所有流程走完之後,我直接在命令行中輸入TS_NODE_PROJECT=XXX.json NODE_ENV=dev webpack --config ./webpack/dev.ts
完美運行,而後將這行命令放到了npm scripts
中:
{ "scripts": { "start": "TS_NODE_PROJECT=XXX.json NODE_ENV=dev webpack --config ./webpack/dev.ts" } }
再次運行npm start
,發現居然出錯了-.-,提示我說import
語法不能被識別,這個很顯然就是沒有應用咱們在ts_NODE_PROJECT
中指定的config
文件。
剛開始並不知道問題出在哪,由於這個在命令行中直接執行並無任何問題。
期間曾經懷疑是不是環境變量沒有被正確設置,還使用了cross-env
這個插件,甚至將命令寫到了一個sh
文件中進行執行。
然而問題依然存在,後來在一個羣中跟小夥伴們聊起了這個問題,有人提出,你是否是全局安裝了ts-node
。
檢查之後發現,果真是的,在命令行執行時使用的是全局的ts-node
,可是在npm scripts
中使用的是本地的ts-node
。
在命令行環境執行時還覺得是會自動尋找父文件夾node_modules
下邊的依賴,實際上是使用的全局包。
乖乖的在client-src
文件夾下也安裝了ts-node
就解決了這個問題。
全局依賴害人。。
測試用例的改造
前邊的Webpack
改成TypeScript
大多數緣由是由於強迫症所致。
可是測試用例的TypeScript
改造則是一個能極大提升效率的操做。
爲何要在測試用例中使用 TypeScript
測試用例使用chai
來編寫,(以前的Postman
也是用的chai
的語法)
chai
提供了一系列的語義化鏈式調用來實現斷言。
在以前的分享中也提到過,這麼多的命令你並不須要徹底記住,只知道一個expect(XXX).to.equal(true)
就夠了。
可是這樣的通篇to.equal(true)
是巨醜無比的,而若是使用那些語義化的鏈式調用,在不熟練的狀況下很容易就會獲得:
Error: XXX.XXX is not a function
由於這確實有一個門檻問題,必需要寫不少才能記住調用規則,各類not
、includes
的操做。
可是接入了TypeScript
之後,這些問題都迎刃而解了。
也是前邊提到的,全部的TypeScript
模塊都有其對應的.d.ts
文件,用來告訴咱們這個模塊是作什麼的,提供了什麼可使用。
也就是說在測試用例編寫時,咱們能夠經過動態提示來快速的書寫斷言,而不須要結合着文檔去進行「翻譯」。
使用方式
若是是以前有寫過mocha
和chai
的童鞋,基本上修改文件後綴+安裝對應的@types
便可。
能夠直接跳到這裏來:開始編寫測試腳本
可是若是對測試用例感興趣,可是並無使用過的童鞋,能夠看下邊的一個基本步驟。
安裝依賴
TypeScript
相關的安裝,npm i -D typescript ts-node
Mocha
、chai
相關的安裝,npm i -D mocha chai @types/mocha @types/chai
- 若是須要涉及到一些API的請求,能夠額外安裝
chai-http
,npm i -D chai-http @types/chai-http
環境的依賴就已經完成了,若是額外的使用一些其餘的插件,記得安裝對應的@types
文件便可。
若是有使用ESLint之類的插件,可能會提示modules
必須存在於dependencies
而非devDependencies
這是ESLint的import/no-extraneous-dependencies
規則致使的,針對這個,咱們目前的方案是添加一些例外:
import/no-extraneous-dependencies: - 2 - devDependencies: - "**/*.test.js" - "**/*.spec.js" - "**/webpack*" - "**/webpack/*"
針對這些目錄下的文件/文件夾不進行校驗。是的,webpack的使用也會遇到這個問題
開始編寫測試腳本
若是是對原有的測試腳本進行修改,無外乎修改後綴、添加一些必要的類型聲明,不會對邏輯形成任何修改。
一個簡單的示例
// number-comma.ts export default (num: number | string) => String(num).replace(/\B(?=(\d{3})+$)/g, ',') // number-comma.spec.ts import chai from 'chai' import numberComma from './number-comma' const { expect } = chai // 測試項 describe('number-comma', () => { // 子項目1 it('`1234567` should transform to `1,234,567`', done => { expect(numberComma(1234567)).to.equal('1,234,567') done() }) // 子項目2 it('`123` should never transform', done => { const num = 123 expect(numberComma(num)).to.equal(String(num)) done() }) })
若是全局沒有安裝mocha
,記得將命令寫到npm script
中,或者經過下述方式執行
./node_modules/mocha/bin/mocha -r ts-node/register test/number-comma.spec.ts # 若是直接這樣寫,會拋出異常提示 mocha 不是命令 mocha -r ts-node/register test/number-comma.spec.ts
mocha
有一點兒比較好的是提供了-r
命令來讓你手動指定執行測試用例腳本所使用的解釋器,這裏直接設置爲ts-node
的路徑ts-node/register
,而後就能夠在後邊直接跟一個文件名(或者是一些通配符)。
目前咱們在項目中批量執行測試用例的命令以下:
{ "scripts": { "test": "mocha -r ts-node/register test/**/*.spec.ts" } }
npm test
能夠直接調用,而不須要添加run
命令符,相似的還有start
、build
等等
一鍵執行之後就能夠獲得咱們想要的結果了,不再用擔憂一些代碼的改動會影響到其餘模塊的邏輯了 (前提是認真寫測試用例)
小結
作完上邊兩步的操做之後,咱們的項目就實現了100%的TypeScript
化,在任何地方享受靜態編譯語法所帶來的好處。
附上更新後的代碼含量截圖:
最近針對TypeScript
作了不少事情,從Node.js
、React
以及此次的Webpack
與Mocha+Chai
。
TypeScript
由於其存在一個編譯的過程,極大的下降了代碼出bug的可能性,提升程序的穩定度。
全面切換到TypeScript
更是可以下降在兩種語法之間互相切換時所帶來的沒必要要的消耗,祝你們搬磚愉快。
以前關於 TypeScript 的筆記
一個完整的 TypeScript 示例
歡迎各位來討論關於TypeScript
使用上的一些問題,針對穩重的感受不足之處也歡迎指出。