1. 程式人生 > >JS的靜態型別檢測,有內味兒了

JS的靜態型別檢測,有內味兒了

我們知道 TypeScript 2.3 以後的版本支援使用--checkJs.js檔案進行型別檢查和錯誤提示。 但是由於 JavaScript 是弱型別語言,在編寫程式碼的時候,是無法檢測變數的型別的。

因此每次執行程式碼型別報錯的時候,我心中都會冒出來一個強烈的願望:要是 JavaScript是強型別的多好!

好訊息是,JSDoc 的 @ts-check,可以現實這個願望。

立即上手

如果能有機會使用 TypeScript 那當然是最好,但是往往開發的老專案在早期都是 JavaScript 完成的,如果都遷移到 TypeScript 版本工作量是龐大的,而且不可避免出現許多bug問題,那麼有沒有一種方式可以無痛的在使用JavaScript的同時享受到TypeScript的型別檢查呢?

答案就是 // @ts-check,在 js 檔案的頭部引入這樣一行註釋,然後配合JSDoc就可以在JavaScript程式碼中使用 TypeScript的型別檢查了。

舉個例子,在下圖中我們首先聲明瞭一個變數 a,然後把數字 1 賦給了它,接著又把字串 '1' 賦給了它,看起來好像沒有什麼問題,而且執行起來也不會報錯。

let a = 1;
a = 'a';

然後我們加上 // @ts-check 試試:

// @ts-check

/**
 * @type {Number}
 */
let a = 1;
a = '1';

神奇的一幕出現了,在變數a賦值的下面,出現了紅色波浪線,滑鼠放上去提示:

let a: number
@type — {Number}
不能將型別“"1"”分配給型別“number”。ts(2322)

也就是說我們將一個字串賦值給了一個數字型別的變數是有問題的,這個時候我們未執行程式,但是編輯器已經幫我們分析出了程式碼可能存在的問題,這時候我們執行程式碼,是沒有報錯的。

因為這個型別檢測只是讓我們按照TypeScript的強型別語言檢測型別問題,但是我們依然是JavaScript程式碼依然會按照JavaScript的程式碼邏輯執行,如是TypeScript程式碼的話,這裡執行就會報錯。

JSDoc 型別標記

既然ts-check這麼好用,我們來看看 JSDoc 型別的註釋支援哪些型別的檢測。

根據官方文件,JSDoc現在支援下面幾個型別檢測:

  • @type
  • @param (or @arg or @argument)
  • @returns (or @return)
  • @typedef
  • @callback
  • @template
  • @class (or @constructor)
  • @this
  • @extends (or @augments)
  • @enum

下面我們選擇常用的標記進行說明,更多更詳細的標記可以參考官方文件。

@type

描述:用來宣告變數的型別。

/** 
 *  - string型別
 * @type {string}
 */
let a1;

/** 
 *  - windows物件型別
 * @type {Window}
 */
let a2;
 
/** 
 * - string或者boolean型別
 * @type {string | boolean}
 */
let sb;


// -------- 多種方式指定陣列型別--------
/** @type {number[]} */
var ns;
/** @type {Array.<number>} */
var nds;
/** @type {Array<number>} */
var nas;

// ---- 還可以指定物件字面量型別。 例如,一個帶有a(字串)和b(數字)屬性的物件---
/** @type {{ a: string, b: number }} */
var var9;

@param和@returns

描述:@param語法和@type相同,但增加了一個引數名。

/**
 * 宣告函式引數型別
 * @param {string}  p1 - p1 是 string 型別引數
 * @param {string=} p2 - p2 是可選的 string 型別引數
 * @param {string} [p3] - 另外一種可選引數寫法
 * @param {string} [p4="test"] - p4 是可選的 string 型別引數(預設值為 "test")
 * @return {string} - 函式返回值是 string 型別
 */
function fn3(p1, p2, p3, p4){
  // TODO
}


/**
 * 用 “return” 說明函式的返回值型別
 * @return {number}
 */
function fn1() {}

/**
 * 可以像使用 "@return" 一樣使用 "@returns"
 * @returns {{a: string, b: number}}
 */
function fn2() {}

@typedef

描述:@typedef 可以用來聲明覆雜型別,和@param類似的語法。

/**
 * 用 "@typedef" 自定義複雜型別
 * @typedef {Object} SpecialType - 建立一個新的型別 'SpecialType'
 * @property {string} prop1 - SpecialType 屬性 prop1 是 string 型別
 * @property {number} prop2 - SpecialType 屬性 prop2 是 number 型別
 * @property {number=} prop3 - SpecialType 屬性 prop3 是可選的 number 型別
 * @prop {number} [prop4] - SpecialType 屬性 prop4 是可選的 number 型別
 * @prop {number} [prop5=42] - SpecialType 屬性 prop5 是可選的 number 型別(預設值 42))
 */
/** @type {SpecialType} */
let specialTypeObject;

可以在第一行上使用objectObject

實驗要求

經測試,在 VSCodeIDEA下可以直接使用ts-check 的型別檢測,sublime等編輯器不可以,應該是要下載對應的外掛才可以。

寫在最後

對於老專案,使用 // @ts-checkJSDoc 來來享受TypeScript型別系統的好處是最簡單、學習成本最低的方法。

而對於新專案,則更加推薦直接使用 TypeScript 來進行程式碼編寫,並且各大框架裡面都是用的TypeScript進行的程式碼編寫,在可期的未來,TypeScript將會越來越受歡迎。