2013-03-12 68 views
4

註解的JavaScript我在一個實習醫生文件下面的typedef:對於關閉編譯問題

/** @typedef ({eventNameArray: Array.<string>,eventArrayIndex: number}) */ 
var triggerNextData; 

想它被用作傳遞參數triggerNext功能(將觸發下一個事件)。 eventNameArray(字符串數組)和eventArrayIndex都是必需的。

這裏是triggerNext功能預計類型:

/** 
* @type {function(triggerNextData)} 
*/ 
triggerNext: function(data){ 
... 
} 

當我把它像這樣:

mediator.triggerNext("hi there"); 

我得到預期警告,但警告說,eventNameArray是可選的:

找到:string required:{eventArrayIndex:number,eventNameArray: (Array。| null)} mediator.triggerNext(「hi there」);

不知何故,它沒有拿起它需要一個字符串數組(數組類型沒有顯示),該數組是可選的。

下不會產生任何警告:

mediator.triggerNext({eventNameArray:null,eventArrayIndex:0}); 
mediator.triggerNext({eventNameArray:[22,33],eventArrayIndex:0}); 

我想知道如何鍵入eventNameArray作爲字符串數組強制性,可以這樣做?如果是的話,我會如何做到這一點?

+1

幾件事情,使用一個基於構造函數的對象而不是typedef(http://closuretools.blogspot.co.uk/2012/02/type-checking-tips.html),也需要註釋一個! 。 – lennel 2013-03-12 07:55:43

+0

謝謝您的建議。太糟糕的blogspot在中國被阻止,因此無法閱讀。我看到很多關於此的文章都在blogspot上,但看不到它們。雖然triggerNext從來沒有用新的triggerNext創建。它是一個對象的屬性,如var mediator = {triggerNext:function(... – HMR 2013-03-12 08:57:25

+0

將粘貼整個文章給你一個答案。 – lennel 2013-03-12 09:26:46

回答

2

從closuretools.blogspot.co.uk/2012/02/type-checking-tips.html複製逐句,由於「謝謝你的建議。太糟糕的blogspot在中國封鎖,所以不能讀取。我看到很多關於這個的文章都在blogspot上,但看不到它們「

Closure編譯器的類型語言有點複雜。它有聯合(「變量x可以是A或B」),結構函數(「變量x是一個返回數字的函數」)和記錄類型(「變量x是任何具有屬性foo和bar的對象」)。

很多人告訴我們,這仍然沒有足夠的表現力。有很多方法可以編寫不適合我們類型系統的JavaScript。人們建議我們應該添加mixins,特徵以及匿名對象的事後命名。

這對我們來說並不特別令人驚訝。 JavaScript中的對象規則有點像Calvinball的規則。你可以改變任何事情,並隨着時間的推移制定新的規則。許多人認爲一個好的類型系統爲你提供了一個強大的方式來描述你的程序的結構。但它也給你一套規則。類型系統確保每個人都同意什麼是「類」,什麼是「界面」,什麼是「是」。當你試圖將類型註釋添加到無類型的JS時,你不可避免地會遇到我頭腦中的規則與頭腦中的規則不匹配的問題。沒關係。

但是我們很驚訝,當我們給人這種類型的系統時,他們經常會找到多種方式來表達同樣的東西。有些方法比其他方法更好。我想我會寫這個來描述人們嘗試的一些事情,以及他們是如何制定出來的。

功能與函數()

有兩種方法來描述函數。一種是使用{Function}類型,編譯器將其解釋爲「任何對象x,其中'x instanceof Function'爲true」。 A {Function}是故意糊塗。它可以接受任何參數,並返回任何內容。你甚至可以使用'新'。編譯器會讓你在不發出警告的情況下調用它。

結構函數更加具體,並且可以對函數的功能進行細化控制。 A {function()}不需要任何參數,但我們不在乎它返回的結果。 A {function(?): number}返回一個數字並只需要一個參數,但我們不關心該參數的類型。當你用「新」來調用它時,A {function(new:Array)}創建一個數組。我們的類型文檔和JavaScript風格指南有更多關於如何使用結構函數的例子。

很多人問我們,{Function}是不鼓勵,因爲它不是特定的。其實,這非常有用。例如,請考慮Function.prototype.bind的定義。它可以讓你的咖喱函數:你可以給它一個函數和一個參數列表,它會給你一個帶有「預先填充的」參數的新函數。我們的類型系統不可能表達返回的函數類型是第一個參數類型的轉換。因此,Function.prototype.bind上的JSDoc表示它返回{Function},編譯器必須使用手動編碼邏輯來確定真實類型。

還有很多情況下,您想通過回調函數來收集結果,但結果是特定於上下文的。

rpc.get(‘MyObject’, function(x) { 
    // process MyObject 
}); 

的「rpc.get」的方法是一個很大的笨拙,如果你通過了回調參數有型投什麼就愈大。所以,只給參數一個{Function}類型通常會更簡單,並且相信調用者類型不值得進行類型檢查。

對象與匿名對象

很多JS庫定義一個全局對象,有很多的方法。該對象應該具有哪種類型的註釋?

var bucket = {}; 
/** @param {number} stuff */ bucket.fill = function(stuff) {}; 

如果你來自Java,你可能會試圖給它鍵入{Object}。

/** @type {Object} */ var bucket = {}; 
/** @param {number} stuff */ bucket.fill = function(stuff) {}; 

這通常不是你想要的。如果添加「@type {Object}」註釋,則不僅僅是告訴編譯器「bucket是Object」。您告訴它「bucket允許爲任何Object」。因此,編譯器必須假定任何人都可以將任何對象分配給「桶」,並且該程序仍然是類型安全的。

相反,你最好使用@const。

/** @const */ var bucket = {}; 
/** @param {number} stuff */ bucket.fill = function(stuff) {}; 

現在我們知道,桶不會被分配給任何其他對象,而編譯器的類型推理引擎可以使剷鬥及其方法強得多檢查。

一切都能成爲記錄類型嗎?

JavaScript的類型系統並不那麼複雜。它有8種特殊語法:null,undefined,boolean,number,string,Object,Array和Function。有些人已經注意到,記錄類型可以讓你定義「一個具有屬性x,y和z的對象」,並且該類型定義允許你給任何類型表達式命名。因此,在這兩者之間,您應該能夠使用記錄類型和typedef定義任何用戶定義的類型。這就是我們需要的一切嗎?

當您需要一個函數來接受大量的可選參數時,記錄類型非常棒。所以,如果你有這樣的功能:

/** 
* @param {boolean=} withKetchup 
* @param {boolean=} withLettuce 
* @param {boolean=} withOnions 
*/ 
function makeBurger(withKetchup, withLettuce, withOnions) {} 

你可以把它有點容易調用是這樣的:

/** 
* @param {{withKetchup: (boolean|undefined), 
      withLettuce: (boolean|undefined), 
      withOnions: (boolean|undefined)}=} options 
*/ 
function makeBurger(options) {} 

這種運作良好。但是當你在一個程序的許多地方使用相同的記錄類型時,事情會變得有點多毛。假設您爲makeBurger的參數創建了一個類型:

/** @typedef {{withKetchup: (boolean|undefined), 
       withLettuce: (boolean|undefined), 
       withOnions: (boolean|undefined)}=} */ 
var BurgerToppings; 

/** @const */ 
var bobsBurgerToppings = {withKetchup: true}; 

function makeBurgerForBob() { 
    return makeBurger(bobsBurgerToppings); 
} 

後來,Alice在Bob的庫上創建了一個餐廳應用程序。在一個單獨的文件中,她嘗試添加洋蔥,但是搞砸了API。

bobsBurgerToppings.withOnions = 3; 

關閉編譯器會注意到bobsBurgerToppings的BurgerToppings記錄類型不再匹配。但它不會抱怨Alice的代碼。它會抱怨鮑勃的代碼造成類型錯誤。對於非平凡的節目,鮑勃可能很難找出爲什麼這些類型不再匹配。

一個好的類型系統不僅僅表達關於類型的契約。它也爲我們提供了一種在代碼違約時分配責任的好方法。因爲一個類通常定義在一個地方,編譯器可以找出誰負責打破類的定義。但是,當你有一個匿名對象被傳遞給許多不同的函數,並且具有從許多不同的地方設置的屬性時,對於人類和編譯器來說,更難確定誰打破了類型契約。

發佈者尼克·桑托斯,軟件工程師

+0

謝謝。添加!會看看我是否可以使它警告更好。應用程序不是那麼大,所以它不應該太重要,但是對如何使用它並不感興趣。 – HMR 2013-03-12 10:22:50

3

All objects can be null by default

所有的對象類型,默認情況下它們是否與可空操作宣告空。

您可以使用!說這應該是一個非空值:

@typedef ({eventNameArray:!Array.<string>, eventArrayIndex:number}) 

至於數組的元素是字符串,我不知道(但)。

+0

謝謝你,lennel也建議使用!。仍然必須測試它,但認爲它會解決它。由於某些原因,閉包會檢查數組類型,因爲可以指定它,但在使用錯誤類型的數組項時似乎沒有發出警告。 – HMR 2013-03-12 10:24:53

+0

使用push方法時,您在數組中遇到錯誤,並且應該在傳遞給類的構造函數而不是typedef時,正如文章指出的那樣,typedefs有點鬆散,不應用於映射覆雜類型。 – lennel 2013-03-12 10:36:52