1

我正在使用Closure編譯器並嘗試執行錯誤'class'的子類化。我有一個元功能,試圖做到這一點。它看起來像這樣:使用Closure編譯器時擴展錯誤

/** 
* @param {string} name 
* @param {(function(new:Error)?)=} parent 
* @param {(Function?)=} constructor 
* @return {function(new:Error)} 
* @usage 
*  var MyError = subclassError('MyError', null, function (x, y) { 
*   this.message = prop1 + " " + prop2; 
*  }); 
*  throw new MyError(new Error(), 1, 2); 
**/ 
function subclassError (name, parent, constructor) { 
    // allow subclassing of other errors 
    if (!parent) parent = Error; 
    // allow no constructor to be provided 
    if (!constructor) { 
     /** @this {Error} */ 
     constructor = function (msg) { if (msg) this.message = msg; }; 
    } 
    /** 
    * @constructor 
    * @extends {Error} 
    * @param {Error} error 
    * @param {...*} var_args 
    **/ 
    var func = function (error, var_args) { 
     // this check is just a guard against further errors 
     // note that we don't use something like getOwnPropertyNames 
     // as this won't work in older IE 
     var propsToCopy = ['fileName', 'lineNumber', 'columnNumber', 
      'stack', 'description', 'number', 'message']; 
     for (var i = 0; i < propsToCopy.length; i++) { 
      this[propsToCopy[i]] = error[propsToCopy[i]]; 
     } 
     this.name = name; 
     constructor.apply(this, Array.prototype.slice.call(arguments, 1)); 
    }; 
    func.prototype = Object.create(parent.prototype); 
    func.prototype.constructor = func; 
    func.name = constructor.name; 
    return func; 
} 

基本上,上述函數創建錯誤,其中,當所謂的要求通過在天然Error對象的子類。從這個對象中填充行號,堆棧跟蹤等等,但它也允許你傳入其他參數。

下面是使用它的一個樣本:

/** 
* @constructor 
* @extends {Error} 
* @param {Error} err 
* @param {number} bar 
* @param {number} baz 
**/ 
var FooError = subclassError('FooError', null, function (bar, baz) { 
    this.message = "invalid bar: '" + bar + "', (using '" + baz + "')"; 
}); 

/** 
* Frobs the noid. 
* @param {number} x 
* @param {number} y 
* @throws FooError 
**/ 
function frob (x, y) { 
    if (x < 0) throw new FooError(new Error(), x, y); 
} 

當我編譯這個用封像這樣:

java -jar compiler.jar 
    --compilation_level ADVANCED_OPTIMIZATIONS --warning_level VERBOSE 
    --language_in ECMASCRIPT5 --language_out ECMASCRIPT3 
    --js_output_file=frob.min.js frob.js 

我得到以下警告:

frob.js:39: WARNING - inconsistent return type 
found : function (new:func, (Error|null), ...*): undefined 
required: function (new:Error): ? 
    return func; 
     ^

frob.js:60: WARNING - Function FooError: called with 3 
     argument(s). Function requires at least 0 argument(s) and no more 
     than 0 argument(s). 
    if (x < 0) throw new FooError(new Error(), x, y); 

之一這裏棘手的事情是,雖然我知道(從代碼中)func對象從下降因爲Error的後代通過Error作爲父母,Closure認爲它是func的實例,並且這不是Error的實例。我試着在上面加入@extends {Error}來糾正問題,但Closure仍然認爲!(func instanceof Error)。這是第一個警告。

在第二個警告中,問題是它不承認FooError有三個參數。我試圖添加三個參數到FooError,但由於Closure沒有看到參數列表,所以無法找到它。

有沒有辦法擺脫這些警告告訴閉幕更多的信息?還是有一種方法可以在Closure中以更簡單的方式擴展Error,以便我們獲得行號,堆棧等?

+0

是否有你沒從'Error'對象使用標準的原型繼承一​​個特別的原因? 'function FooError(){}; FooError.prototype = new Error();' –

+1

沒關係 - 我忘了Error對象有多少痛苦。 –

回答

1

因爲您正在調用一個返回構造函數的方法,所以爲了在編譯器中進行完整類型檢查,我們必須有點棘手。

首先,改變subclassError方法返回未知類型,這樣我們就可以獨立定義類型簽名:

/** 
* @param {string} name 
* @param {(function(new:Error)?)=} parent 
* @param {(Function?)=} constructor 
* @return {?} 
**/ 
function subclassError (name, parent, constructor) {} 

接下來,我們添加一個存根定義FooError這樣編譯器可以理解的類型信息。然後我們實際分配subclassError方法的結果。

/** 
* @constructor 
* @extends {Error} 
* @param {Error} err 
* @param {number} bar 
* @param {number} baz 
**/ 
var FooError = function(err, bar, baz) {}; 

FooError = subclassError('FooError', null, function (bar, baz) { 
    this.message = "invalid bar: '" + bar + "', (using '" + baz + "')"; 
}); 

現在我們得到正確的類型檢查由編譯器

// The compiler will warn that FooError is called with 
// the wrong number of arguments 
new FooError(new Error()); 

See a Full Example

+0

我希望我能避免做存根,但這仍然是一個很棒的答案。如果可以的話,我會+10。謝謝你的知識淵博,乍得! –

+0

另一個想法是,對於這裏涉及的類型檢查複雜性的數量,寫一個存根就像我希望的那樣簡單。效果很好。 –

+1

這就是爲什麼我這樣寫答案的原因。我假設你的構造函數會變化很大。 –