使用 TypeScript 改造構建工具及測試用例

来源:https://www.cnblogs.com/jiasm/archive/2018/09/03/9577715.html
-Advertisement-
Play Games

最近的一段時間一直在搞 ,一個巨硬出品、賦予 語言靜態類型和編譯的語言。 第一個完全使用 重構的純 項目已經上線並穩定運行了。 第二個前後端的項目目前也在重構中,關於前端基於 的`TypeScript`套路之前也有提到過: "TypeScript在react項目中的實踐" 。 但是這些做完以後也總感 ...


最近的一段時間一直在搞TypeScript,一個巨硬出品、賦予JavaScript語言靜態類型和編譯的語言。
第一個完全使用TypeScript重構的純Node.js項目已經上線並穩定運行了。
第二個前後端的項目目前也在重構中,關於前端基於webpackTypeScript套路之前也有提到過:TypeScript在react項目中的實踐

但是這些做完以後也總感覺缺了點兒什麼 (沒有盡興)


是的,依然有五分之一的JavaScript代碼存在於項目中,作為一個TypeScript的示例項目,表現的很不純粹。
所以有沒有可能將這些JavaScript代碼也換成TypeScript呢?
答案肯定是有的,首先需要分析這些代碼都是什麼:

  • Webpack打包時的配置文件
  • 一些簡單的測試用例(使用的mocha和chai)

知道了是哪些地方還在使用JavaScript,這件事兒就變得很好解決了,從構建工具(Webpack)開始,逐個擊破,將這些全部替換為TypeScript

Webpack 的 TypeScript 實現版本

在這8102年,很幸福,Webpack官方已經支持了TypeScript編寫配置文件,文檔地址
除了TypeScript以外還支持JSXCoffeeScript的解釋器,在這就忽略它們的存在了

依賴的安裝

首先是要安裝TypeScript相關的一套各種依賴,包括解釋器及該語言的核心模塊:

npm install -D typescript ts-node

typescript為這個語言的核心模塊,ts-node用於直接執行.ts文件,而不需要像tsc那樣會編譯輸出.js文件。

ts-node helloworld.ts

因為要在TypeScript環境下使用Webpack相關的東東,所以要安裝對應的types
也就是Webpack所對應的那些*.d.ts,用來告訴TypeScript這是個什麼對象,提供什麼方法。

npm i -D @types/webpack

一些常用的pLugin都會有對應的@types文件,可以簡單的通過npm info @types/XXX來檢查是否存在

如果是一些小眾的plugin,則可能需要自己創建對應的d.ts文件,例如我們一直在用的qiniu-webpack-plugin,這個就沒有對應的@types包的,所以就自己創建一個空文件來告訴TypeScript這是個啥:

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打包時使用的配置。

  1. 將上述的配置文件改名為其它名稱,Webpack文檔示例中為tsconfig-for-webpack-config.json,這裡就直接沿用了
  2. 然後添加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只有三個有效值productiondevelopemntnone,而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

因為這確實有一個門檻問題,必須要寫很多才能記住調用規則,各種notincludes的操作。
但是接入了TypeScript以後,這些問題都迎刃而解了。
也是前邊提到的,所有的TypeScript模塊都有其對應的.d.ts文件,用來告訴我們這個模塊是做什麼的,提供了什麼可以使用。
也就是說在測試用例編寫時,我們可以通過動態提示來快速的書寫斷言,而不需要結合著文檔去進行“翻譯”。


使用方式

如果是之前有寫過mochachai的童鞋,基本上修改文件尾碼+安裝對應的@types即可。
可以直接跳到這裡來:開始編寫測試腳本
但是如果對測試用例感興趣,但是並沒有使用過的童鞋,可以看下邊的一個基本步驟。

安裝依賴

  1. TypeScript相關的安裝,npm i -D typescript ts-node
  2. Mochachai相關的安裝,npm i -D mocha chai @types/mocha @types/chai
  3. 如果需要涉及到一些API的請求,可以額外安裝chai-httpnpm 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命令符,類似的還有startbuild等等

一鍵執行以後就可以得到我們想要的結果了,再也不用擔心一些代碼的改動會影響到其他模塊的邏輯了 (前提是認真寫測試用例)

小結

做完上邊兩步的操作以後,我們的項目就實現了100%的TypeScript化,在任何地方享受靜態編譯語法所帶來的好處。
附上更新後的代碼含量截圖:

最近針對TypeScript做了很多事情,從Node.jsReact以及這次的WebpackMocha+Chai
TypeScript因為其存在一個編譯的過程,極大的降低了代碼出bug的可能性,提高程式的穩定度。
全面切換到TypeScript更是能夠降低在兩種語法之間互相切換時所帶來的不必要的消耗,祝大家搬磚愉快。

之前關於 TypeScript 的筆記

一個完整的 TypeScript 示例

typescript-example

歡迎各位來討論關於TypeScript使用上的一些問題,針對穩重的感覺不足之處也歡迎指出。

參考資料


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 很奇怪,為什麼稍微低了一點就無法運行 解決辦法:npm install --save-dev @angular/cli@latest 重新安裝最新的版本 ...
  • 作為前端最火的構建工具,是前端自動化工具鏈 最重要的部分 ,使用門檻較高。本系列是筆者自己的學習記錄,比較基礎,希望通過 問題 + 解決方式 的模式,以前端構建中遇到的具體需求為出發點,學習 工具中相應的處理辦法。(本篇中的參數配置及使用方式均基於 ) 一. webpack與自動化測試 對應的關鍵詞 ...
  • 輸入url用戶按下回車鍵 主要的流程下麵這張圖已經非常清晰了。下圖主要通過Navigation Timing API在客戶端收集性能數據,並用XMLHttpRequest 或其它技術傳送到服務端。同時,該API使你可以衡量之前難以獲取的數據,如卸載前一個頁面的時間,在功能變數名稱解析上的時間,在執行load ...
  • 一、 .parse :將字元串轉為js對象;JSON.parse(str, function(key,value){}) .stringify:該方法用於將js對象轉為json字元串.使用方式:JSON.stringify(obj, fn) 二、判斷數組的四種方式: prototype constr... ...
  • 下載地址 完成後效果 ...
  • let的特性: const的特性: 凍結對象: ...
  • globalCompositeOperation即Canvas中的合成操作。 1、source-over 這是預設值,他表示繪製的圖形將畫在現有畫布之上 2、destination-over 這個操作的值與前一個值相反,所以現在目標繪製在源之上 3、source-atop 這個操作會將源繪製在目標之 ...
  • 前言 在上一篇博文中介紹了Vue.js的常用指令,今天總結歸納一下彈窗Dialog的使用,彈窗經常被使用在一些表單的增刪改查啊,或者彈出一些提示信息等等,在保留當前頁面狀態的情況下,告知用戶並承載相關操作。 之前做了表格的增刪改查任務,其中用到了dialog彈窗,今天總結歸納一下Vue.js中幾種彈 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...