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
And it says: 2376 A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties.
I understand that it's because of test property. but can you allow not-this code before super() call how ES6 does?
Also, this won't work too, of course:
classInheritsextendsSuper{test=1;view:Element=document.createElement('span');constructor(){super(this.view);// 2332 'this' cannot be referenced in current location.};}
There are similar restrictions in most OO languages so that the super-class constructor does not run with fields set to unexpected values.
How this related to non-this code?
You can however do this:
Or provide appropriate getters in the parent class.
How this is different from non-this code in constructor?
Your both ways are ugly and looks more like a hack here. I do not actually ask what I can do here to workaround it, I ask why that case is not allowed.
You can call super whenever you want if you don't have member initializers:
// This code does not have any errorsclassInheritsextendsSuper{test;// changed, was 'test = 1;'view: Element;constructor(){letview=document.createElement('span');super(view);this.test=1;this.view=view;};}
If you look at how class member initializers are emitted, the reasoning for this becomes fairly clear. Member initializers are emitted after the super call (otherwise your derived-class initializers wouldn't properly overwrite base class initializations); by enforcing the super call to be the first thing the compiler can guarantee that there's exactly one (correct) place to emit those initializers.
You move the super call to any position by removing member initializers -- this makes explicit what you expect the initialization order of each class member to be.
You can call super whenever you want if you don't have member initializers:
I know that, but it's not a reason to close this issue. It seems more like you was lazy to detect if super() was called before any this access or not. No offense here please, you are closing this issue -- I am saying truth as is.
The code should (if we can make normative statements about things that aren't allowed) emit 10, 15. What do you propose the compiler emit here? Or by what rule would this code be disallowed?
I suggest ignore such side effects and follow ES2015 spec here, because you cannot save developers from all of them and this "save" looks pretty strange.
So I think the answer is clear: emit property initialization section of derived class immediately after _super.call(), i.e. I agree with Arthur Stolyar (@NekR) .
by enforcing the super call to be the first thing the compiler can guarantee that there's exactly one (correct) place to emit those initializers.
Actually TS had failed to enforce "the super call to be the first thing", because developers can place almost arbitrary code in the parameter list of a super call, which executes before the super call. Java etc. also had failed to enforce it.
So the "super call statement first" rule is not quite useful; disallowing access to this/super before/in a super call should be sufficient.
The proposal is to remove the restriction on super being the first statement in the constructor body, and only require the existence of at least one call to super.
Sorry if I'm misunderstanding something, but is this issue actually fixed (as opposed to "by design")? Should we be allowed to initialize subclass members outside the constructor or not, if the super call comes later in the constructor than the first line? The code in the OP comment gives me the same error today as was reported two years ago.
Because class member initializers are emitted immediately following the super call, the super call needs to be in a predictable location. Otherwise unpredictable effects would occur:
It looks like m would get the value 1 here, but it would actually get the value 2 if we unconditionally allowed pre-super code in classes with property initializers.
Ryan Cavanaugh (@RyanCavanaugh) Thanks for your answer. So, was anything "fixed" in this issue? Seems like it should be "declined" or "working as intended".
Is the effect you describe actually unpredictable (i.e., it might change in future versions of TS) or just potentially confusing? If it is unambiguously true that member initializers are evaluated immediately after the super call, then I do expect m to get the value 2.
I see what you mean about it "looking" like it would be 1. But such confusion arises in other situations that are not considered to be errors. What do you make of this very slightly different example:
varx=0;classCextendsB{m=++x;constructor(){super(x++);// super constructor takes a parameter}}constc=newC();
No error from TS, and c.m == 2.
The problem I see here is not with the location of the super call, but with having a weird state dependency between initializer and constructor that makes reasoning correctly about its behavior difficult.
I know there's a permissiveness trade-off between allowing bad code and disallowing good code. There's good code that can be written by allowing both initializers and code before the super constructor. Do you have any more egregious examples of bad code that happens when you allow this?
which would be a legal way to write super() calls according to the ES spec, but now we have to do all kinds of backflips to correctly emit code that accomplishes the same thing.
It's more of a pragmatic thing - the amount of work we'd have to do to handle all the possible places in an expression a super call could appear and properly extract an equivalent program would be a substantial amount of complexity and testing, compared to just limiting users to choosing between property initializers or weird super call locations (which doesn't seem to be a big deal in practice).
If there were some clever 20-line thing to pluck out an equivalent program for any arbitrary set of super calls, sure, but I expect this would be hundreds if not thousands of lines of code + testcases for the sake of a pretty small improvement.
varDerived=(function(_super){__extends(Derived,_super);functionDerived(arg){var_this;var__super=(function(){_this=_super.apply(this,arguments)||this;__super=function(){//ES6 throws if super(…) called twicethrownewReferenceError("super dupe");};// emit initializers here_this.prop='expr';return_this;}).bind(this);// emit constructor body here// super(…) ⇒ __super(…)__super(arg);_this.prop='or not';return_this;}returnDerived;}(Base));
then we don't have to worry about trying to shove initializer statements in multiple or crazy places. The constructor body can be transcribed mostly as-is, and initializers are evaluated exactly once, immediately after the super constructor is called.
This may or may not turn out to be a 20-liner does the right thing. Even if it is, I understand that you probably do not consider the issue important enough to tackle. In that case, could you please change the status of the issue to reflect what's actually going on (that this behavior is either intentional or not important enough to fix)?
If you go back and read this issue from top to bottom, it's clear that Arthur Stolyar (@NekR) and Duan Yao (@duanyao) are asking to always allow code before super(…) in the constructor, and that until mid-August 2015 that's what the discussion is about. Then a few months go by. In October, the issue wakes up and switches to a related but different topic (lexical analysis to prevent this from being referenced before super), and it's marked as "fixed". That is confusing to me and probably will be to any other folks who run into this behavior and find themselves here in the land of TypeScript GitHub Issue #3311 looking for relevant information.
Wow, I thought this was fixed. Why so you mark this fixed if it's not? You're saying about should be, predictable location, etc. But as I stated before, Babel handles this right and was handling it right when I opened this issue, TypeScript still not. I stopped using TypeScript for a while ago because of such small issue which nobody wants to fix because it's their beautiful software (yet advertised to use) and they have so principles of not changing things. It's a shame anyway and I'm sad for your tool's users.
It looks like m would get the value 1 here, but it would actually get the value 2 if we unconditionally allowed pre-super code in classes with property initializers.
Evaluate class members first, then code before super, then super and everything will be as expected. Also that x is a side effect and not safe to use it in that way in general.
Evaluate class members first, then code before super, then super and everything will be as expected. Also that x is a side effect and not safe to use it in that way in general.
You have to initialize class members after super(…) or else the base class constructor may modify them. I'm fairly sure the required order is something like:
evaluate code before super(…)
evaluate super(…)
initialize class members
evaluate code after super(…)
The issue that bothers Ryan Cavanaugh (@RyanCavanaugh) is how to emit class member initialization immediately after the call to super(…) if that call can show up in all sorts of weird places.
The issue that bothers Ryan Cavanaugh (@RyanCavanaugh) is how to emit class member initialization immediately after the call to super(…) if that call can show up in all sorts of weird places.
It was actually discussed already, just like babel does that.
I mostly agree but the Babel REPL doesn't seem to know what do with super(…) in some expression locations when property initializers are present. I think my proposal above might work but I'm not sure.
Activity
qc00 commentedon Jun 1, 2015
There are similar restrictions in most OO languages so that the super-class constructor does not run with fields set to unexpected values.
You can however do this:
Or provide appropriate getters in the parent class.
NekR commentedon Jun 1, 2015
How this related to non-
thiscode?How this is different from non-
thiscode in constructor?Your both ways are ugly and looks more like a hack here. I do not actually ask what I can do here to workaround it, I ask why that case is not allowed.
Do you have valid point why it's not allowed?
RyanCavanaugh commentedon Jun 1, 2015
Linking to #1617 for bookkeeping purposes.
RyanCavanaugh commentedon Jun 1, 2015
You can call
superwhenever you want if you don't have member initializers:If you look at how class member initializers are emitted, the reasoning for this becomes fairly clear. Member initializers are emitted after the super call (otherwise your derived-class initializers wouldn't properly overwrite base class initializations); by enforcing the super call to be the first thing the compiler can guarantee that there's exactly one (correct) place to emit those initializers.
You move the
supercall to any position by removing member initializers -- this makes explicit what you expect the initialization order of each class member to be.NekR commentedon Jun 1, 2015
I know that, but it's not a reason to close this issue. It seems more like you was lazy to detect if
super()was called before anythisaccess or not. No offense here please, you are closing this issue -- I am saying truth as is.Consider this code:
Do you think this is okay code? Why then I need those member definitions before constructor at all?
RyanCavanaugh commentedon Jun 1, 2015
It's not just about
thisaccess. Consider code like this:The code should (if we can make normative statements about things that aren't allowed) emit
10, 15. What do you propose the compiler emit here? Or by what rule would this code be disallowed?NekR commentedon Jun 1, 2015
I suggest ignore such side effects and follow ES2015 spec here, because you cannot save developers from all of them and this "save" looks pretty strange.
See this: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-super-keyword-runtime-semantics-evaluation and this: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-makesuperpropertyreference
Also test case here.
RyanCavanaugh commentedon Jun 1, 2015
Trading correctness for convenience is not something we do lightly.
I don't see the relevance of the linked test case -- there's no property initializer there; TypeScript's behavior is the same is 6to5's.
Again, what do you propose the compiler emit for the example I gave?
NekR commentedon Jun 1, 2015
Wrong. You cannot do the same in TS, you need to do this, which is not the same (see #3311 (comment)):
(es6 example was a bit wrong, here is correct one)
I am pretty sure it should do this.
Again, I do not see difference between these two cases:
[1]
[2]
But in TS you need to have duplicate definitions for first case (see #3311 (comment)) and second is not allowed in TS at all.
duanyao commentedon Jun 2, 2015
Ryan Cavanaugh (@RyanCavanaugh) ,
Your example can be translated to an equivalent valid TS form (replace
xwithDerived1.x):The compiler emits for
Derived1:Converting
Derived1.xback toxgives:So I think the answer is clear: emit property initialization section of derived class immediately after
_super.call(), i.e. I agree with Arthur Stolyar (@NekR) .Actually TS had failed to enforce "the super call to be the first thing", because developers can place almost arbitrary code in the parameter list of a super call, which executes before the super call. Java etc. also had failed to enforce it.
So the "super call statement first" rule is not quite useful; disallowing access to
this/superbefore/in a super call should be sufficient.mhegazy commentedon Aug 18, 2015
The proposal is to remove the restriction on super being the first statement in the constructor body, and only require the existence of at least one call to super.
19 remaining items
jcalz commentedon May 27, 2017
Sorry if I'm misunderstanding something, but is this issue actually fixed (as opposed to "by design")? Should we be allowed to initialize subclass members outside the constructor or not, if the super call comes later in the constructor than the first line? The code in the OP comment gives me the same error today as was reported two years ago.
RyanCavanaugh commentedon May 30, 2017
The code in the OP is correctly an error.
Because class member initializers are emitted immediately following the
supercall, the super call needs to be in a predictable location. Otherwise unpredictable effects would occur:It looks like
mwould get the value1here, but it would actually get the value2if we unconditionally allowed pre-supercode in classes with property initializers.jcalz commentedon May 30, 2017
Ryan Cavanaugh (@RyanCavanaugh) Thanks for your answer. So, was anything "fixed" in this issue? Seems like it should be "declined" or "working as intended".
Is the effect you describe actually unpredictable (i.e., it might change in future versions of TS) or just potentially confusing? If it is unambiguously true that member initializers are evaluated immediately after the
supercall, then I do expectmto get the value2.I see what you mean about it "looking" like it would be
1. But such confusion arises in other situations that are not considered to be errors. What do you make of this very slightly different example:No error from TS, and
c.m == 2.The problem I see here is not with the location of the
supercall, but with having a weird state dependency between initializer and constructor that makes reasoning correctly about its behavior difficult.I know there's a permissiveness trade-off between allowing bad code and disallowing good code. There's good code that can be written by allowing both initializers and code before the super constructor. Do you have any more egregious examples of bad code that happens when you allow this?
Thanks!
RyanCavanaugh commentedon May 31, 2017
It's more that the super call has to be in a position where we can simply and reliably insert assignment statements. For example, you could write
which would be a legal way to write
super()calls according to the ES spec, but now we have to do all kinds of backflips to correctly emit code that accomplishes the same thing.jcalz commentedon May 31, 2017
Ah, so are these backflips something you don't think TS should do in principle, or would they be something you'd consider a proposal/pull request for?
RyanCavanaugh commentedon Jun 1, 2017
It's more of a pragmatic thing - the amount of work we'd have to do to handle all the possible places in an expression a super call could appear and properly extract an equivalent program would be a substantial amount of complexity and testing, compared to just limiting users to choosing between property initializers or weird super call locations (which doesn't seem to be a big deal in practice).
If there were some clever 20-line thing to pluck out an equivalent program for any arbitrary set of super calls, sure, but I expect this would be hundreds if not thousands of lines of code + testcases for the sake of a pretty small improvement.
jcalz commentedon Jun 1, 2017
Right now TS emits
super(…)as_this = _super.call(this, …) || this;and emits the property initializers as statements afterward:But, if we replace calls to
super(…)with calls to another function which both calls the super constructor and then calls the initializers:then we don't have to worry about trying to shove initializer statements in multiple or crazy places. The constructor body can be transcribed mostly as-is, and initializers are evaluated exactly once, immediately after the super constructor is called.
This may or may not turn out to be a 20-liner does the right thing. Even if it is, I understand that you probably do not consider the issue important enough to tackle. In that case, could you please change the status of the issue to reflect what's actually going on (that this behavior is either intentional or not important enough to fix)?
If you go back and read this issue from top to bottom, it's clear that Arthur Stolyar (@NekR) and Duan Yao (@duanyao) are asking to always allow code before
super(…)in the constructor, and that until mid-August 2015 that's what the discussion is about. Then a few months go by. In October, the issue wakes up and switches to a related but different topic (lexical analysis to preventthisfrom being referenced beforesuper), and it's marked as "fixed". That is confusing to me and probably will be to any other folks who run into this behavior and find themselves here in the land of TypeScript GitHub Issue #3311 looking for relevant information.Whew, I wrote a lot. Thanks again for your time!
NekR commentedon Jun 1, 2017
Wow, I thought this was fixed. Why so you mark this fixed if it's not? You're saying about should be, predictable location, etc. But as I stated before, Babel handles this right and was handling it right when I opened this issue, TypeScript still not. I stopped using TypeScript for a while ago because of such small issue which nobody wants to fix because it's their beautiful software (yet advertised to use) and they have so principles of not changing things. It's a shame anyway and I'm sad for your tool's users.
NekR commentedon Jun 1, 2017
Evaluate class members first, then code before
super, thensuperand everything will be as expected. Also thatxis a side effect and not safe to use it in that way in general.jcalz commentedon Jun 1, 2017
You have to initialize class members after
super(…)or else the base class constructor may modify them. I'm fairly sure the required order is something like:super(…)super(…)super(…)The issue that bothers Ryan Cavanaugh (@RyanCavanaugh) is how to emit class member initialization immediately after the call to
super(…)if that call can show up in all sorts of weird places.NekR commentedon Jun 1, 2017
It was actually discussed already, just like babel does that.
jcalz commentedon Jun 1, 2017
I mostly agree but the Babel REPL doesn't seem to know what do with
super(…)in some expression locations when property initializers are present. I think my proposal above might work but I'm not sure.