【问题标题】:Extending Error when using Closure Compiler使用闭包编译器时扩展错误
【发布时间】:2015-12-03 16:56:26
【问题描述】:

我正在使用闭包编译器并尝试对错误“类”进行子类化。我有一个试图做到这一点的元功能。它看起来像这样:

/**
 * @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,让我们能够获取行号、堆栈等?

【问题讨论】:

  • 您没有使用来自Error 对象的标准原型继承是否有特殊原因? function FooError() {}; FooError.prototype = new Error();
  • 没关系 - 我忘记了 Error 对象有多痛苦。

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


【解决方案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

【讨论】:

  • 我希望我可以避免做存根,但这仍然是一个很棒的答案。如果可以的话,我会+10。谢谢你这么有见识,乍得!
  • 再想一想,对于这里涉及的类型检查的复杂性,编写存根与我希望的一样简单。效果很好。
  • 这就是我这样写答案的原因。我假设你的构造函数会变化很大。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多