使用Closure Compiler

时间:2015-12-03 16:56:27

标签: javascript inheritance error-handling google-closure-compiler

我正在使用Closure Compiler并尝试对Error'类'进行子类化。我有一个尝试这样做的元函数。它看起来像这样:

/**
 * @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的子类,当调用它时需要传入一个本地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);
}

当我用Closure编译它时如此:

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的后代都是在或我们使用Error作为父级,Closure认为它是func的一个实例,并且这不是Error的实例。我尝试在上面添加@extends {Error}来解决问题,但Closure仍然认为!(func instanceof Error)。这是第一个警告。

在第二个警告中,问题是它无法识别FooError有三个参数。我试图向FooError添加三个参数,但由于Closure没有看到参数列表,因此无法找到它。

有没有办法通过告诉Closure更多信息来摆脱这些警告?或者有没有办法以更简单的方式在Closure中扩展Error,以便我们获取行号,堆栈等?

1 个答案:

答案 0 :(得分: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