如果类显式扩展Object,为什么不能调用方法?

时间:2019-05-07 12:02:22

标签: typescript

当我在Typescript中有一个显式扩展Object的类时,则尝试调用该类的对象方法会失败:

class Example extends Object {
    constructor() {
        super();
    }
    public getHello() : string {
        return "Hello";
    }
}

let greeter = new Example();
alert(greeter.getHello());

错误:greeter.getHello is not a function。这是为什么?如果我删除了extends子句和super()的调用,那么它将突然起作用。

我的问题是代码是从自定义的JSweet版本自动生成的,而我们仅转换部分代码库。在类层次结构中,不应进行转译的类被简单地映射到Object,因为extends很难在不大量更改JSweet的情况下轻松删除。

2 个答案:

答案 0 :(得分:7)

我认为您可以争论这是TypeScript中的错误。可能不是一个高优先级的问题,但是...:-)

这是因为Object会忽略调用它的this,并返回一个新的空白对象。 TypeScript编译代码的方式(针对ES5环境),最终在Object中这样调用Example

function Example() {
    return Object.call(this) || this;
}

Object.call(this)的结果是一个新对象,就像您做过{}一样。

所以解决方案是...不要这样做。 :-)

  

我的问题是代码是从自定义的JSweet版本自动生成的,而我们仅转换部分代码库。在类层次结构中不应该被转换的类被简单地映射到Object,因为如果不对JSweet进行大量更改就无法轻松删除扩展。

太好了。如果您可以定位到ES2015 +,则问题将消失,因为这仅与TypeScript为ES5和更早版本创建的版本有关。如果您不能这样做,恐怕听起来您想在the TypeScript issues list上提交问题,并可能提出带有修复的请求请求。 (我一直在寻找现有报告,但没有找到。)这与扩展其他内置插件(ErrorArray)略有不同,因为有一个非常简单的解决方案:只需忽略extends子句。

或者,正如您在评论中提到的那样,您可以拥有一个不执行任何操作的伪类,例如:

class FauxObject { }

,然后使用它代替Objectclass Example extends FauxObject)。


为了详细起见,完整的编译版本是这样,对Object的调用由******标记:

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Example = /** @class */ (function (_super) {
    __extends(Example, _super);
    function Example() {
        return _super.call(this) || this;             // ******
    }
    Example.prototype.getHello = function () {
        return "Hello";
    };
    return Example;
}(Object));
var greeter = new Example();
alert(greeter.getHello());

_superObject

答案 1 :(得分:0)

提交bug-report后,我被提示到a FAQ entry that addresses extending typescript built-ins

  

在ES2015中,返回对象的构造函数将super(...)的任何调用者隐式替换为this的值。对于生成的构造函数代码,有必要捕获super(...)的任何潜在返回值并将其替换为该值。

     

结果,错误,数组和其他子类可能不再按预期工作。这是由于Error,Array等的构造函数使用ECMAScript 6的new.target来调整原型链的事实。但是,在ECMAScript 5中调用构造函数时,无法确保new.target的值。默认情况下,其他下层编译器通常具有相同的限制。

说明与T.J. Crowder's answer类似,此外,他们建议了其他解决方法:

  

建议,您可以在调用任何super(...)之后立即手动调整原型。

在我的情况下,这是将Object.setPrototypeOf(this, Example.prototype);添加到构造函数中(或说服JSweet这样做)。

但是

  

很遗憾,这些变通办法不适用于 Internet Explorer 10 及更低版本。可以将方法从原型手动复制到实例本身(即FooError.prototype到实例),但是原型链本身无法固定。

编辑:由于发现在我们的JSweet项目中更容易实现,因此我最终使用了此变通方法。