Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new myVariable.constructor() doesn't compile #4356

Closed
jklmli opened this issue Aug 19, 2015 · 16 comments
Closed

new myVariable.constructor() doesn't compile #4356

jklmli opened this issue Aug 19, 2015 · 16 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@jklmli
Copy link

jklmli commented Aug 19, 2015

class Test {
    num: number;

    constructor(num: number) {
        this.num = num;
    }
}

var t = new Test(1);
var u = new t.constructor(2);

This code throws a compile error, but appears to produce valid code when entered into http://www.typescriptlang.org/Playground.

EDIT:
This code correctly compiles after changing the last line to:

var u = new (<{ new(...args: Array<any>): Test }>t.constructor)(2);
@DanielRosenwasser DanielRosenwasser added the By Design Deprecated - use "Working as Intended" or "Design Limitation" instead label Aug 19, 2015
@DanielRosenwasser
Copy link
Member

The compiler will still emit code in the presence of errors unless given the --noEmitOnError flag. Emit in the face of syntactic errors is a best-effort emit with no guarantees in syntactic validity.

@jklmli
Copy link
Author

jklmli commented Aug 19, 2015

This code should be supported on all browsers, given https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor.

I appreciate the --noEmitOnError flag when doing development, but this doesn't help when our CI blocks deploy when there's a compile error, and there's no option to disable errors on lines (a la tslint).

I strongly object to the By Design tag here, since a better design would be to avoid triggering a compile error at all. The posted workaround is unnecessarily verbose.

@jklmli jklmli changed the title new myVariable.constructor() doesn't work new myVariable.constructor() doesn't compile Aug 19, 2015
@DanielRosenwasser
Copy link
Member

I apologize, I misread the original issue. I thought you were suggesting that the code should not have been emitted at all.

@DanielRosenwasser DanielRosenwasser removed the By Design Deprecated - use "Working as Intended" or "Design Limitation" instead label Aug 19, 2015
@ahejlsberg
Copy link
Member

The constructor property will only be correct if the constructor function chooses to initialize it. Although TypeScript generates code to do so, it is not a requirement and you can't generally rely on it for an arbitrary constructor function (e.g. one implemented in a JavaScript framework). For that reason TypeScript doesn't strongly type the constructor property.

@RyanCavanaugh
Copy link
Member

The constructor property will only be correct if the constructor function chooses to initialize it

I can't find any evidence supporting this. All runtimes seem to hook this up automatically.

This code should be supported on all browsers, given https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor.

I can't find any evidence supporting this, either. The ES spec doesn't define that this property is present as a result of object creation.

http://www.ecma-international.org/ecma-262/6.0/#sec-evaluatenew
http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
http://www.ecma-international.org/ecma-262/6.0/#sec-objectcreate

@jklmli
Copy link
Author

jklmli commented Aug 24, 2015

MDN lists the relevant specs near the bottom of the page - this is the most relevant:
http://www.ecma-international.org/ecma-262/6.0/#sec-object.prototype.constructor

@RyanCavanaugh
Copy link
Member

@jiaweihli but all that says is that the actual field Object.prototype.constructor points to Object. It doesn't imply anything about whether or not new'd values automatically get a constructor property.

@jklmli
Copy link
Author

jklmli commented Aug 26, 2015

@RyanCavanaugh I believe the spec is a bit unclear here - it says the field Object.prototype.constructor points to %Object%, which I think refers to the constructor, since it's subclassable.

This has been the stance of MDN since the earliest version of that page, so that's most likely their interpretation of the ES1 spec. The relevant section begins on page 73 here.

I don't necessarily have the knowledge needed to resolve this ambiguity, but I think it's worth looking into this since a lot of freely available documentation lists the behavior I mentioned above. Anecdotally, the code has worked for me in several different environments, and is also supported by all major browsers.

@kitsonk
Copy link
Contributor

kitsonk commented Aug 26, 2015

@jiaweihli appears to be right. Given the following in Chrome 44:

class A {
    constructor () {
        console.log('I was called');
    }
}

let a = new A();
let aa = new a.constructor();

a.constructor references A and works as expected.

@RyanCavanaugh
Copy link
Member

I get that this works in many runtimes, but I'm still not seeing it being spec'd behavior. Why does the fact that Object.prototype.constructor === Object mean that a.constructor === A ?

@kitsonk
Copy link
Contributor

kitsonk commented Aug 27, 2015

Correct me if I am wrong, but doesn't %Object% refer to the intrinsic value of the current realm, ergo the current execution context, therefore a.constructor === A:

19.1.3.1 Object.prototype.constructor

The initial value of Object.prototype.constructor is the intrinsic object %Object%.

And intrinsic objects are described as:

Within this specification a reference such as %name% means the intrinsic object, associated with the current Realm, corresponding to the name.

@mhegazy
Copy link
Contributor

mhegazy commented Aug 27, 2015

Pinging @bterlson for this one.

@bterlson
Copy link
Member

I think you can strongly type constructor for ES6 classes. The constructor property is added to the C.prototype in ClassDefinitionEvaluation step 18. It is configurable, though, so users could presumably delete it (GeneratorFunction instances, notably, do not have a constructor property on their [[prototype]] for reasons users might also find compelling - not leaking a reference to the constructor when vending instances IIUC).

People above are also correct that Object.prototype has a constructor property that points to Object (and thus any object with Object.prototype in its prototype chain and no other constructor properties will have a .constructor property pointing to Object). But I don't think this has any bearing on whether you can strongly type the .constructor property of class instances.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Aug 28, 2015
@RyanCavanaugh
Copy link
Member

Duplicate of #3841

@RyanCavanaugh RyanCavanaugh added Duplicate An existing issue was already created and removed In Discussion Not yet reached consensus labels Sep 28, 2015
@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus and removed Duplicate An existing issue was already created labels Mar 7, 2016
@RyanCavanaugh
Copy link
Member

Reopening since #3841 is for the static side whereas this is for the instance side

@RyanCavanaugh
Copy link
Member

Let's track at #4586

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

7 participants