1

Closed

default constructor in sub-classes do not support factory constructors

description

JavaScript supports factory constructors (constructors that return something other than this), and TypeScript does appear to support/accept them too:
class Foo {
    constructor() {
        return something_else();
    }
}
However, the auto-generated default constructor in a subclass does not correctly support factory constructors:
class Bar extends Foo {}
The compiled output looks like this:
var Foo = (function () {
    function Foo() {
        return something_else();
    }
    return Foo;
})();

var Bar = (function (_super) {
    __extends(Bar, _super);
    function Bar() {
        _super.apply(this, arguments);
    }
    return Bar;
})(Foo);
new Bar() doesn't work as expected - it returns an instance of Bar, rather than inheriting the return value from the parent factory-constructor.

In order to support factory constructors, the compiled Bar class should look like this:
var Bar = (function (_super) {
    __extends(Bar, _super);
    function Bar() {
        return _super.apply(this, arguments) || this;
    }
    return Bar;
})(Foo);
Or in other words, if the parent constructor returns something (other than null/undefined) it's a factory constructor, and we should return that instead of this.
Closed Mon at 10:18 PM by jonturner
As part of our move to GitHub, we're closing our CodePlex suggestions and asking that people move them to the GitHub issue tracker for further discussion. Some feature requests may already be active on GitHub, so please make sure to look for an existing issue before filing a new one.

You can find our GitHub issue tracker here:
https://github.com/microsoft/typeScript/issues

comments

danquirk wrote Nov 7, 2013 at 7:03 PM

This doesn't seem universally applicable. something_else() isn't necessarily a factory method that's deferring to some other logic for construction. It could simply be an initialization function specifically for Foo that Bar doesn't want anything to do with. It feels to me like if you're trying to use a factory pattern like this you'd just want to be explicit about its use in a derived class anyway.

** Closed by danquirk 11/07/2013 12:03PM

mindplay wrote Nov 7, 2013 at 9:22 PM

This doesn't seem universally applicable. something_else() isn't necessarily a factory method that's deferring to some other logic for construction.
something_else() isn't the factory constructor - the constructor is... that method is just there to point out the fact that this constructor returns something other than this.
It could simply be an initialization function specifically for Foo that Bar doesn't want anything to do with.
I don't see your point. If the return-value of something_else() wasn't what you wanted from this factory constructor, you wouldn't use the return statement, and it would work like a regular constructor.
It feels to me like if you're trying to use a factory pattern like this you'd just want to be explicit about its use in a derived class anyway.
You would need to be explicit about it, if you actually implemented a constructor in the derived class - but since that's optional, if the parent class uses a factory constructor, it seems really unnatural not to inherit that behavior in the default constructor.

If you didn't implement a constructor, you should expect the constructor to behave like the parent constructor - in the same way you expect any other method to behave like the parent method if you don't override it.

I don't see how this is not universally applicable.

This is a JavaScript feature, plain and simple, and the default constructor breaks this feature.

Having given this some more thought, I also think the following should be supported:
class Foo {
    constructor(...): Bar {
        return new Bar(...);
    }
}
Why? Because it models a real JavaScript feature.

You already have constructor signatures permitted as part of interfaces:
class Bar {}

interface BarFactory {
    new(): Bar;
}

class Foo /* implements BarFactory */ {
    constructor() /* : Bar */ {
        return new Bar();
    }
}
I shouldn't have to comment out these statements, because having multiple constructor signatures with different return types is possible with plain JavaScript.

To further prove my point: I can already write the above code using plain JavaScript, and write a .d.ts file for it, declaring my constructor signatures, and it'll work with TypeScript - you already have this behavior supported by the type-system, it's just not properly supported by the code-generator.

What you have currently is not "universally applicable", because it breaks inheritance of factory constructors.