TypeScript: Incorrect transpiled constructor of derived classes

  • Missing own class methods
  • Missing relationship with own class (#10166)

TypeScript Version: master

Code

class C extends Error {
    constructor() {
        super('error');
    }
    m() {
    }
}
console.log(new C().message);
console.log(new C().m);
console.log(new C() instanceof Error);
console.log(new C() instanceof C);

Expected behavior:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var C = (function (_super) {
    __extends(C, _super);
    function C() {
        var _that = _super.call(this, 'error');
        if (_that) {
            _that.__proto__ = this;
        }
        var _this = _that || this;
        return _this;
    }
    C.prototype.m = function () {
    };
    return C;
}(Error));
console.log(new C().message); // 'error'
console.log(new C().m); // [Function]
console.log(new C() instanceof Error); // true
console.log(new C() instanceof C); // true

Actual behavior:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var C = (function (_super) {
    __extends(C, _super);
    function C() {
        return _super.call(this, 'error') || this;
    }
    C.prototype.m = function () {
    };
    return C;
}(Error));
console.log(new C().message); // 'error'
console.log(new C().m); // undefined
console.log(new C() instanceof Error); // true
console.log(new C() instanceof C); // false

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 1
  • Comments: 38 (32 by maintainers)

Commits related to this issue

Most upvoted comments

My recommendation to get this working is to set the prototype explicitly on your instances. for instance:

class MyError extends Error {
    sayHello() {
        return "hello " + this.message;
    }
    constructor(m) {
        super(m);

        // Set the prototype explictilly
        Object.setPrototypeOf(this, MyError.prototype);
    }
}


var e = new MyError("my error");

console.log("e instanceof Error => " + (e instanceof Error)); // true
console.log("e instanceof MyError => " + (e instanceof MyError)); // true
console.log("e.message  => " + (e.message)); // "my error"
console.log("e.sayHello()  => " + (e.sayHello()));  // "hello my error"

The only change here from extending non-built-in classes is Object.setPrototypeOf(this, MyError.prototype);

alternatively, you can just set this.__proto__ instead of using Object.setPrototypeOf`

All subclasses which return a truthy value lose their methods.

@falsandtru Yes, they should lose methods.

In node 6:

class A {constructor() {return {}}};
(new A) instanceof A // false

@ahejlsberg @sandersn @vladima @RyanCavanaugh @DanielRosenwasser @mhegazy Can you resolve these problems before releasing 2.1? I believe TypeScript must not publish these problems via stable versions. At least these problems will break some code because of missing subclass methods.

Others can read about this breaking change and its solution on our wiki

Is the answer to this issue to simply not use instanceof?

Yes. We shouldn’t use instanceof with Error and similar tricky classes without using native feature support of the class extending. We cannot handle/maintain that behavior.

The thing is that extending Error and the like were previously broken as well, but in different ways. As a workaround, you can manually patch the prototype in your constructor, though I know that this is a less-than-satisfying answer.

@falsandtru can you elaborate on the issue you mentioned when inheriting two levels deep or more?

__proto__ is not universally supported

I think so, but this solution possibly has no regression. So TypeScript can fix this bug using this solution. Additionally, the latest stable version doesn’t have this bug. So this bug will be the large regression, and it is too large to release as a stable version.

@falsandtru, one thing should be mentioned is that your expected behavior will generate a weird prototype chain.

However, since it’s hard to be fully compatible. That’s acceptable temporarily.