使用闭包编译器时扩展错误

Extending Error when using Closure Compiler

本文关键字:扩展 错误 编译器 闭包      更新时间:2023-09-26

我正在使用闭包编译器,并尝试对Error"class"进行子类化。我有一个元函数试图做到这一点。它看起来像这样:

/**
 * @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);
}

当我用这样的闭包编译这个时:

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,使我们能够获得行号、堆栈等。?

因为您正在调用一个返回构造函数的方法,所以为了在编译器中进行完整的类型检查,我们必须有点棘手。

首先,更改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());

请参阅完整的示例