Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it not possible to call method if a class explicitely extends Object?

Tags:

typescript

When I have a class in Typescript that explicitely extends Object, then trying to call an object method of that class fails:

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

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

Error: greeter.getHello is not a function. Why is that? If I remove the extends-clause and the super() call, then it suddenly works.

My problem is that the code is autogenerated from a customized JSweet version, and we only transpile some part of the codebase. Classes in the class hierarchy that should not be transpiled are simply mapped to Object, because the extends cannot be easily removed without changing JSweet heavily.

like image 498
hunger Avatar asked May 07 '19 12:05

hunger


1 Answers

I think you could argue that this is a bug in TypeScript. Not a high-priority one, probably, but... :-)

It's because Object ignores the this it's called with, and returns a new, blank object. The way TypeScript compiles that code (when targeting ES5 environments), it ends up calling Object like this in Example:

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

The result of Object.call(this) is a new object, as though you did {}.

So the solution is...don't do that. :-)

My problem is that the code is autogenerated from a customized JSweet version, and we only transpile some part of the codebase. Classes in the class hierarchy that should not be transpiled are simply mapped to Object, because the extends cannot be easily removed without changing JSweet heavily.

Ouch. If you can target ES2015+, the problem goes away as this only relates to the version TypeScript creates for ES5 and earlier. If you can't, I'm afraid it sounds like you'll want to file an issue on the TypeScript issues list and perhaps do a pull request with a fix. (I went looking for an existing report and didn't find one.) This is slightly different from extending other built-ins (Error, Array) in that there's a very simple solution: Just ignore the extends clause.

Or, as you mentioned in a comment, you could have a dummy class that doesn't do anything, like:

class FauxObject { }

and then use that instead of Object (class Example extends FauxObject).


Just for detail's sake, the full compiled version is this, the call to Object is marked by ******:

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());

_super is Object in your case.

like image 151
T.J. Crowder Avatar answered Oct 05 '22 05:10

T.J. Crowder