Constructor functions that aren't constructor functions

Topics: Language Specification
Sep 5, 2013 at 3:27 PM
Edited Sep 5, 2013 at 3:28 PM
A couple of issues have been raised recently to do with placing access modifiers on constructor functions (one raised by myself):

https://typescript.codeplex.com/workitem/1639
https://typescript.codeplex.com/workitem/1591

These issues were closed with comments indicating that the compiler is working as intended in these cases.

Current behaviour when placing an access modifier against a constructor function is that it ceases to be a constructor function and simply becomes a function called "constructor".

e.g.
class test1 {
  constructor() {
    // a constructor
  }
}

class test2 {
  public constructor() {
    // a function called constructor
  }
}

class test3 {
  constructor() {
    // a constructor
  }

  private constructor() {
    // a function called constructor
  }
}
Compiles to:
var test1 = (function () {
    function test1() {
        // a constructor
    }
    return test1;
})();

var test2 = (function () {
    function test2() {
    }
    test2.prototype.constructor = function () {
        // a function called constructor
    };
    return test2;
})();

var test3 = (function () {
    function test3() {
        // a constructor
    }
    test3.prototype.constructor = function () {
        // a function called constructor
    };
    return test3;
})();
This behaviour seems extremely unintuitive. Anyone who hasn't come across this behaviour before will assume that the constructor function in 'test2' is that class's actual constructor. The class 'test3' looks like it should raise a "Multiple constructor implementations are not allowed." error.

It's an unnecessary pitfall for new Typescript developers to fall into given that no compile errors or warnings are generated but the code is unlikely to run as the developer anticipated.

I'm not sure there's a case to be made for ever naming a function "constructor" thats not a constructor.

e.g.
new test2();
test2.constructor(); // there's definitely a better name for whatever this function does
Is there a reason behind the current implementation or is it simply a foible of the compiler?

If access modifiers aren't going to be supported for constructors a compile time error seems a better solution than the current odd behaviour.
Sep 26, 2013 at 2:25 PM
Edited Sep 26, 2013 at 5:19 PM
The constructor property on a prototype object has a special role in JavaScript. A function's prototype has its constructor property set to reference the function itself.
function MyConstructor() {};
MyConstructor.prototype.constructor === MyConstructor; // true
Also, this constructor property is not enumerable by default.

This, however, is broken in situations where we build a prototype chain and assign the prototype property explicitly.
function MyOtherConstructor() {};
MyOtherConstructor.prototype = new MyConstructor();
MyOtherConstructor.prototype.constructor === MyOtherConstructor; // false
This is why a common pattern is to set the constructor property on the prototype manually. This is what TypeScript's __extends function does (besides other things):
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
So in the scheme of things, I wonder what the purpose of allowing to override the constructor property on the prototype is?

If I am not mistaken, the constructor property does not serve any specific purpose in prototype based inheritance (e.g. instanceof checks against the prototype property, not the constructor property), but it allows to reference the constructor function on an object from 'user' code.

Also, if I remember correctly the TS compiler used to fail on "constructor" definitions that had access modifier in earlier versions of the compiler (0.8?).

It would be interesting to hear more about the reasoning of the current implementation.

Maybe the straight-forward answer is that it follows the ES.next spec?
Coordinator
Sep 26, 2013 at 4:57 PM
We've decided that 'public constructor' will just be the same as 'constructor' going forward. 'private constructor' will be an error (for now).