JavaScript 採用動態類型,在編譯期不對類型進行檢查,等真正執行的時候由運行時來判斷類型是否出現錯誤,這種特性有點像彙編裏的計算同樣,類型很弱,不一樣類型老是顯式、隱式地轉換。javascript
在一個普通的 Todo Demo 裏類型或許沒有那麼重要,而當項目變得龐大起來的時候,裏邊有不少不一樣的邏輯,當項目由另一我的接手的時候,閱讀就很麻煩,由於老是要查看代碼上下文的各個變量的類型(自定義類型或者原生類型),通常來講,在前端 console.log 一下就能夠看到了,但也只是一個臨時的辦法,治標不治本。css
固然,強類型的 JavaScript 已經很成熟了,那就是 TypeScript,但並非全部項目一開始就是 TS 寫的,也有純 JS 的項目,那麼在這些項目裏如何維護好類型?html
答案是在 VSCode 下使用 JSDoc;VSCode 會盡最大努力推導出類型出來的。前端
JSDoc 其實就是代碼註釋,在這種註釋裏能夠標明 JavaScript 裏值的類型,以及對代碼的註釋,一般能夠在代碼裏看到以 @
符開頭的標註,那就是 JSDoc 了。如下是一個簡單的例子java
/** * 加法 * @param { Number } x * @param { Number } y * @returns { Number } */
function add(x, y) {
return x + y;
}
複製代碼
上面 @param
, @returns
就說明了參數 x y 以及函數的返回值的類型。git
參考 VSCode - type-checking 能夠顯式的打開 JS 的類型檢查:github
打開類型檢查能夠看到傳入其餘類型的參數的時候會報錯,而填入了正確的類型的值以後,VSCode 就能知道 z
是什麼類型的了(由於 x, y 均爲數字,x + y 也理所固然的是數字)express
也就能實現代碼補全提示了:數組
@param
後能夠接 String
Number
Array
Object
等等。 如:函數
/** * * @param { Array<Number> } numbers * @param { String[] } names */
function test(numbers, names) {
names.forEach(name => {
// ...
})
}
複製代碼
上面的 String[]
的寫法其實與 Array<String>
是同樣的,表示了一個元素爲字符串的數組,並且 VSCode 一樣能代碼自動補全 forEach
方法以及 name
的方法來(String.pototype)。
而下面的例子表示一個自定義的對象:
/** * say hello * @param { { name: String } } person */
function sayHello(person) {
console.log(person.name);
}
複製代碼
@param
標明函數的參數的類型;@returns
標明函數的類型。
其實在上面的例子裏就有了關於自定義類型的說明(對象的那個),但上述的那個例子是針對字面量的聲明,如今介紹一下 VSCode 對 JavaScript 類的處理。
class Person {
/** * Person * @param { String } name */
constructor(name) {
this.name = name;
}
/** * @returns { Person } */
sayHello() {
console.log('Hello, i am', this.name);
return this;
}
/** * @param { Person } friend * @returns { Person } */
playWith(friend) {
this.sayHello();
console.log('and i will play with', friend.name);
return this;
}
}
複製代碼
在這裏聲明瞭 Person
類,上面有方法 sayHello
還有一個 name
屬性。在 VSCode 看來,這也是一種類型,能夠做爲 @param
的參數使用(上例的 playWith
函數)
順便一提,JSDoc 裏的類型標註的語法主要來自於 Google Clojure Compile 裏的類型表達式。
JSDoc 一樣提供了必定能力的 泛型
,但這種泛型限制很大,跟真正的泛型差的比較遠。
回到剛剛的例子 add(x, y)
若是 x y 限定死了數字,那字符串類型的 x y 的相加是否還須要從新編寫一次 add 呢?其實不用,可使用 @template
的方式來完成。
/** * 加法 * @template T * @param { T } x * @param { T } y * @returns { T } */
function add(x, y) {
return x + y;
}
複製代碼
此處引入了 T
,能夠這樣理解這段 JSDoc:
x
和 y
的類型都是 T
,說明它們的類型是同樣的,返回值也同樣。@template
來聲明 T,那 VSCode 會認爲它是構造函數 T 的類型,此處的 @template 聲明是說明 T 是「泛型」,它是一種特殊的變量,只用於表示類型而不是值在使用了 @template 以後:
這說明,d 和 z 的類型須要經過 VSCode
理解了 add
的 JSDoc
以後才能推導出來,而 T
彷佛是一個 變量
,在上面兩種狀況下分別爲 string
和 number
。
還有一種類型標註的方法即便用 *.d.ts
文件,一般普通的 JavsScript 庫是不帶類型標註的,這樣會不兼容 TypeScript 裏的類型檢查,所以須要引入 d.ts
用於描述 js 庫的類型讓 ts 可以使用。
固然,VSCode 能夠讀取它,並方便開發。編寫 test.js:
/** * @param { Person } p */
function test(p) {
}
複製代碼
而後編寫 Person 的類型描述 test.d.ts
declare interface Person {
name: string,
age?: number,
sayHello: () => Person,
playWith: (friend: Person) => Person
}
複製代碼
注:age 的問號表明年齡無關緊要。
而後 VSCode 會找到 *.d.ts 文件並讀取,獲得類型信息:
http://www.css88.com/doc/jsdoc/about-block-inline-tags.html https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler#type-expressions https://code.visualstudio.com/Docs/languages/javascript#_type-checking https://zh.wikipedia.org/wiki/JavaScript