-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Return value of super() calls not used for this
#7574
Comments
Looking at the Typescript spec, it seems like there's an explicit incompatibility between section 4.9.1 and ECMA-262 12.3.5.1 Specifically the line "The type of a super call expression is Void": https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#4.9.1 |
If you |
@jeffmcaffer This isn't necessarily true, For instance, this should fully type check: class A {
static _singleton : A;
constructor() : A {
if (!_singleton) {
_singleton = this;
}
return _singleton;
}
} @mhegazy I'd like to point out that Typescript is in direct violation of the ECMAScript spec, and because of this you won't be able to write Custom Elements in Typescript compiled to ES5*. I hope that if this behavior is "By Design" that the design can still be changed so that Typescript correctly implements ES2016 and is actually a superset rather a separate language. *Targeting ES2015 should probably work, because ES2015 just works this way. |
can you elaborate? |
Here's the spec for the callable HTMLElement constructor: https://w3c.github.io/webcomponents/spec/custom/#htmlelement-constructor The relevant parts are step 5-10
The native implementations of Custom Elements will return something that needs to become Separately, the initial polyfill code I'm writing looks something like this: window.HTMLElement = function() {
if (_newInstance) {
var i = _newInstance;
_newInstance = null;
_newTagName = null;
return i;
}
var tagName = // some stuff to emulate new.target
return document.createElement(tagName);
} (actual code here: https://github.com/webcomponents/webcomponentsjs/blob/v1/src/CustomElements/v1/CustomElements.js#L48 ) Test of Typescript ES5 output that fails: https://github.com/webcomponents/webcomponentsjs/blob/v1/tests/CustomElements/v1/js/typescript.js#L15 Corresponding test of Babel output that passes: https://github.com/webcomponents/webcomponentsjs/blob/v1/tests/CustomElements/v1/js/babel.js#L15 |
Compiling to |
Simplified another case: new class extends class {
constructor() {
return function () {};
}
} {
constructor() {
super();
}
} This code returns a function on chrome, firefox, and edge, but TypeScript is not. |
Any news here? Chrome, Safari, and the Web Components polyfills are all making progress on implementing custom elements v1 APIs, which rely on spec-compliant SuperCall behavior. |
@DanielRosenwasser should be presenting this again and investigating issues we need to address. |
Has this been fixed for TypeScript 2.0@beta? Would really like to use TypeScript with Custom Elements V1. |
@supermoos if you target ES6 it works fine. Then, if you need, you can compile to ES5 with Babel. |
@justinfagnani oh yeah, you're right. I tried the build from this readme.md:
I know it's a 14 days old build, just wanted to put it out there :-) |
One issue I'm seeing now is that even if you set the value of class A {
constructor() {
return { a: 10 };
}
}
class B extends A {
foo() {
return this.a;
}
}
new B().foo()
I think that behavior would be very surprising for TypeScript users, but I don't know of a workaround for that. |
That would also be surprising for a JavaScript user, so I'd recommend that people don't do it :) More seriously, TypeScript could catch this: class A {
constructor() {
return { a: 10 }; // OK, because {a: number} is assignable to A
}
}
class B extends A {
foo() {
return this.a; // Warning: {a: number} not assignable to B
}
}
new B().foo() Again, the most common use-case for this behavior is to return an instance of the class with the "interesting" constructor. For browsers, the HTMLElement constructor returns a non-this instance of HTMLElement during parsing. Since return something other that |
My original comment wasn't about the fact that TypeScript currently can't handle that behavior at the type level. There's no way to know from a construct signature whether a So people might extend from In fact, from reading this page I'm not sure how There's definitely more to this than I currently understand. Any chance you can elaborate on that last part @justinfagnani? |
The custom element spec specially makes the result of In the general case of constructors returning new objects, the returned object does not have the prototype chain set to the constructor. |
@Arnavion is correct. In the HTML spec, parser-created elements work like this:
You can see the same steps in the polyfill here: https://github.com/webcomponents/webcomponentsjs/blob/v1/src/CustomElements/v1/CustomElements.js#L568 The important bits with some added comments: // the element instance has already been created, and the definition looked up
_upgradeElement(element, definition, callConstructor) {
const prototype = definition.constructor.prototype;
element.__proto__ = prototype; // 3. Set the prototype
if (callConstructor) {
this._setNewInstance(element); // 4. Store the element in a global
new (definition.constructor)(); // 5. Invoke constructor
element['_upgradedProp'] = true;
console.assert(this._newInstance == null);
} Inside HTMLElement's constructor we have: if (customElements._newInstance) {
const i = customElements._newInstance;
customElements._newInstance = null;
return i; // 6. Return the element from the global
} Even though in the general case an object returned from a constructor might not have the correct prototype chain, in this critical case it will. |
If a constructor returns a value other than
this
, then in subclassessuper()
calls to that constructor should use the result asthis
in the subclass constructor.I believe this is specified in 12.3.5.1, step 10 of SuperCall: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-super-keyword
Getting this behavior correct is going to be very important for supporting Custom Elements, which takes advantage of this to initialize browser-allocated elements with user-written constructors. See https://w3c.github.io/webcomponents/spec/custom/#htmlelement-constructor
Note, Babel 6 implements this correctly.
TypeScript Version:
1.8.9
Code
Expected behavior:
No assertion error
The constructor for
Bar
should compile to this:Actual behavior:
Assertion error
The constructor for Bar compiles to this:
The text was updated successfully, but these errors were encountered: