You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
No compile error thrown when this referenced before call to super completes #8060
Expected behavior:
Typescript should guard against the use of this as the call to super has not completed. Thus it should not compile.
Actual behavior:
Typescript compiles this successfully. It works fine when the target is set to es5 but breaks the browser when target is set to es6.
Error is: VM89:55 Uncaught ReferenceError: this is not defined
This code isn't necessarily invalid -- it's entirely possible (i.e. we can't see its implementation) that the super constructor doesn't invoke the method immediately, in which case thiswould be valid to access at a later date.
ReferenceError: |this| used uninitialized in arrow function in class constructor
It is because it is a lambda function and therefore it is trying to access the lexical this, which isn't available until after super completes, irrespective of what is actually in the super constructor. Therefore, you cannot use lambda function as arguments of a super call if they reference this in ES6 (or all 3 browser manufactures have mis-implemented the spec).
The specific use case that Tom Dye (@tomdye) and I ran into was a situation where we were extending Promise and passing in a lambda function as the executor, which gets invoked immediately. So we were going merrily along, targeting ES5 and then when we tried to target ES6, it broke.
But that is really difficult to guard against, I suspect.
It will only break code that's invalid ES6 in the first place though. If they were ever to try setting the compile target to 'es6' they would see this error at runtime. Must be better to catch it at compile time now rather than down the line should they choose to compile to 'es6' at a later date.
The problem is we won't be catching at compile time -- people will upgrade to the next TypeScript version, recompile to ES5, and their code will start breaking at runtime with some very confusing errors and emit that looks like a bug.
Yes, agreed. It is clearly a breaking change, but one that preserves the runtime semantics. Maybe there is room for a linter to flag uses of lexical this within the scope of a super call (Adi Dahiya (@adidahiya)), since it could be uninitialized?
Just a note on this, the emit in master (TS 2.1) now breaks these examples in ES5 at run-time, so this now has parity. It still does not warn you that it is invalid.
// this.tsclassA{constructor(protectedcb: ()=>void){}}classGreeterextendsA{constructor(){// ^ there should be `var _this = this;`super(()=>{this})}}
// compiled_with_ts2_0_10.jsvar__extends=(this&&this.__extends)||function(d,b){for(varpinb)if(b.hasOwnProperty(p))d[p]=b[p];function__(){this.constructor=d;}d.prototype=b===null ? Object.create(b) : (__.prototype=b.prototype,new__());};varA=(function(){functionA(cb){this.cb=cb;}returnA;}());varGreeter=(function(_super){__extends(Greeter,_super);functionGreeter(){var_this=this;// ^ there should be `var _this = this;`_super.call(this,function(){_this;});}returnGreeter;}(A));
// compiled_with_ts2_1_1.jsvar__extends=(this&&this.__extends)||function(d,b){for(varpinb)if(b.hasOwnProperty(p))d[p]=b[p];function__(){this.constructor=d;}d.prototype=b===null ? Object.create(b) : (__.prototype=b.prototype,new__());};varA=(function(){functionA(cb){this.cb=cb;}returnA;}());varGreeter=(function(_super){__extends(Greeter,_super);functionGreeter(){return// ^ there should be `var _this = this;`_super.call(this,function(){_this;})||this;}returnGreeter;}(A));
Activity
RyanCavanaugh commentedon Apr 13, 2016
This code isn't necessarily invalid -- it's entirely possible (i.e. we can't see its implementation) that the super constructor doesn't invoke the method immediately, in which case
thiswould be valid to access at a later date.kitsonk commentedon Apr 13, 2016
Ryan Cavanaugh (@RyanCavanaugh) the ES6 code:
Fails in Chrome with:
And in Edge:
And Firefox:
It is because it is a lambda function and therefore it is trying to access the lexical this, which isn't available until after
supercompletes, irrespective of what is actually in the super constructor. Therefore, you cannot use lambda function as arguments of a super call if they reference this in ES6 (or all 3 browser manufactures have mis-implemented the spec).RyanCavanaugh commentedon Apr 13, 2016
This works for me in Chrome and Edge?
kitsonk commentedon Apr 13, 2016
Hmmm... wow, ok, I can break it again if I call it within the constructor:
The specific use case that Tom Dye (@tomdye) and I ran into was a situation where we were extending
Promiseand passing in a lambda function as the executor, which gets invoked immediately. So we were going merrily along, targeting ES5 and then when we tried to target ES6, it broke.But that is really difficult to guard against, I suspect.
kitsonk commentedon Apr 13, 2016
Bryan Forbes (@bryanforbes) just suggest that given:
If the emit was:
Then the runtime behaviour of ES5 would match ES6.
RyanCavanaugh commentedon Apr 13, 2016
That seems like a good solution, though I'm slightly afraid of breaking existing working code.
tomdye commentedon Apr 13, 2016
It will only break code that's invalid ES6 in the first place though. If they were ever to try setting the compile target to 'es6' they would see this error at runtime. Must be better to catch it at compile time now rather than down the line should they choose to compile to 'es6' at a later date.
RyanCavanaugh commentedon Apr 13, 2016
The problem is we won't be catching at compile time -- people will upgrade to the next TypeScript version, recompile to ES5, and their code will start breaking at runtime with some very confusing errors and emit that looks like a bug.
kitsonk commentedon Apr 13, 2016
Yes, agreed. It is clearly a breaking change, but one that preserves the runtime semantics. Maybe there is room for a linter to flag uses of lexical
thiswithin the scope of asupercall (Adi Dahiya (@adidahiya)), since it could be uninitialized?RyanCavanaugh commentedon Jun 9, 2016
Accepting PRs once the new transform-based emitter merges. Warning, this is likely a difficult change.
kitsonk commentedon Nov 4, 2016
Just a note on this, the emit in master (TS 2.1) now breaks these examples in ES5 at run-time, so this now has parity. It still does not warn you that it is invalid.
Given:
The emit is...
Version 2.1.0-dev.20161103
Version 2.0.6
zemlanin commentedon Nov 19, 2016
One more case (https://gist.github.com/zemlanin/a306e8498d05c923cac405c047014029):
[#312] Upgrade to TypeScript 2.1.1
Harley-Adams commentedon Mar 3, 2017
I've also found this with creating an arrow function before calling super.
You can see here that we the output has
var _this = this;which shouldn't be valid