為你的 JavaScript 專案新增智慧提示和型別檢查

逆葵發表於2019-03-07

本文首發於我的部落格(點此檢視),歡迎關注。

前言

最近在做專案程式碼重構,其中有一個要求是為程式碼新增智慧提示和型別檢查。智慧提示,英文為 IntelliSense,能為開發者提供程式碼智慧補全、懸浮提示、跳轉定義等功能,幫助其正確並且快速完成編碼。說起來,JavaScript 作為一門動態弱型別解釋型語言,變數宣告後可以更改型別,並且型別在執行時才能確定,由此容易產生大量程式碼執行中才能發現的錯誤,相比 Java 等靜態型別語言,開發體驗上確實差了一截。更煩躁的是,智慧提示就是依賴於靜態型別檢查的,所以在以前,指望 JavaScript 的智慧提示完善度追上 Java 基本不可能。當然,時代在進步,TypeScript 已經問世許久,為 JavaScript 帶來了靜態型別檢查以及其他諸多特性。JavaScript 的智慧提示也已有了解決方案。調研了一段時間後,下文以 VSCode 編輯器作為開發工具,介紹一下如何為 JavaScript 加上智慧提示以及型別檢查。

基於 JSDoc

JSDoc 是目前最通用的 JavaScript API 文件生成器,根據其語法編寫程式碼註釋,可以十分方便地自動生成文件。由於 JSDoc 能提供詳細的型別資訊,其也被 VSCode 等編輯器接受應用於智慧提示。例如,可以使用 @type 標籤來賦予部分宣告的 object 一個特殊型別:

/**
 * @type {{a: boolean, b: boolean, c: number}}
 */
var x = {a: true};
x.b = false;
x. // <- 由於 type 宣告,"x" 將被提示含有屬性 a,b 以及 c
複製程式碼

JSDoc 最常見的使用是為函式的引數宣告型別,使用 @param 來完成:

/**
 * @param {string} param1 - 這裡可以用於解釋引數含義
 */
function Foo(param1) {
    this.prop = param1; // param1 (以及 this.prop)均為 string 型別
}
複製程式碼

為程式碼新增 JSDoc 註釋使得閱讀和理解程式碼更加方便(程式碼交接時再也不用抓狂了,當然前提是註釋寫得好),也保障了開發時的體驗並且降低了很多執行時才能發現的資料型別方面的 bug。VSCode 基本支援 JSDoc 的常見語法,具體使用可參見JSDoc support in JavaScript

基於 TypeScript 型別宣告檔案

除了使用 JSDoc 提前宣告型別,更為激進的做法是直接使用微軟開發的 TypeScript,為整個專案帶來完善的靜態型別檢查。當然,對於老專案來說,改造的成本較為巨大(使用 Flow 也類似,要動的程式碼太多,況且 Flow 爛尾了)。不過由於和 TypeScript 師出同門,VSCode 能夠直接讀取前者的型別宣告檔案,來為 JavaScript 提供智慧提示(實際上 JavaScript 的智慧提示功能就是基於 TypeScript 團隊為 VSCode 提供的 JavaScript 語言服務開發的)。 TypeScript 的型別宣告檔案以 .d.ts 為字尾,用於描述同名的 JavaScript 檔案匯出程式碼的型別,功能上類似於 C 語言的 .h 標頭檔案。不嚴格地來說,ts 型別宣告檔案就像用 TypeScript 語法將 JSDoc 的註釋重寫了一遍並提取到了單獨的檔案中。VSCode 更是將二者作了融合,當你二者混用的時候,可以直接在 JSDoc 的註釋中直接使用 ts 型別宣告檔案中定義的 interface 和 class 等。直接使用官方提供的示意圖(圖中是 Visual Studio,不過無傷大雅):

為你的 JavaScript 專案新增智慧提示和型別檢查

對於自己的程式碼,可以編寫對應的 ts 型別宣告檔案,而對於引用的第三方庫,社群同樣提供瞭解決方案:DefinitelyTyped 提供了常見的第三方庫的型別宣告檔案。VSCode 有很多第三方庫已經內建型別宣告檔案,自己下載的話直接使用 npm 即可:

# @types + 第三方庫名稱
npm i @types/express
複製程式碼

關於 ts 型別宣告檔案的語法等相關資訊,參見官網介紹

另外,在 VSCode 中,型別檢查並非預設開啟,這意味著即使你有詳盡的 JSDoc 註釋或 ts 型別宣告檔案,依然可能在資料型別上栽跟頭。開啟方式為在專案根目錄下新增 jsconfig.json 檔案,並設定 "checkJs": true,示例如下:

{
    "compilerOptions": {
        "checkJs": true
    },
    
    // 位於此目錄下的檔案不進行靜態檢查和智慧提示
    "exclude": [
        "node_modules",
        "**/node_modules/*"
    ]
}
複製程式碼

總結

最後,無論是對老專案的改造或是新專案的開發,使用以上的方式新增智慧提示和型別檢查顯而易見會略微拖慢開發速度,但我們認為,與智慧提示帶來的開發體驗、將很多可能在執行時才能發現的錯誤通過型別檢查前置解決、順手完成的詳細文件以及重構程式碼時的信心相比,這點速度的犧牲是值得的。

參考文件:

JavaScript in Visual Studio Code

Working with JavaScript

JavaScript Language Service in Visual Studio

相關文章