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
Incorrect output for #private with nested class in ESNext #41788
But this feels like a lot of work for the matter at hand. Maybe the simplest solution would be to make target:ESnext imply useDefineForClassFields:true to side-step the problem entirely. Alternatively, an easy/quick solution to achieve correctness would be to degrade the target:ESnext && useDefineForClassFields:false case to use down-levelling.
Until this is fixed, users of target:ESNext can workaround this by enabling useDefineForClassFields
I like Rob Palmer (@robpalme)'s idea of ESNext implying useDefineForClassFields; the emit is simpler and spec-standard class properties are widely enough supported that people overusing target:esnext [1] are unlikely to run into problems.
For the explicit combination of (1) target:esnext (2) useDefineForClassFields: false (3) nested static class with private reference, we should add an error that says "you can't do this, change useDefineForClassFields: true, or target an earlier version of ES."
[1] for example, somebody who just wants to use every feature in JS without the compiler doing any downlevelling, but who hasn't thought too much about which browser will.
Reacted by ExE Boss, Ryan Cavanaugh, Rob Palmer, Daniel Ehrenberg, Titian Cernicova-Dragomir and Thomas Chetwin
If target:esnext,then useDefineForClassFields: true will now be the default.
If target:esnext, useDefineForClassFields: true we emit correct code so no further action is needed (ex)
If target:esnext, useDefineForClassFields: false and we encounter a usage of a #private field in a static member we emit an error like "You can't do this, change useDefineForClassFields: true, or target an earlier version of ES."
I would strongly discourage using delete, as that is a performance cliff in V8 and other engines and implementers have strongly advised not to do this.
One approach I've seen is to leverage a computed property name, if possible:
This, of course, doesn't work if the class has no non-private members, in which case we might have to add an unused member, as you propose, but don't delete it.
var<tempvariables>;
class A {
#x;// NOTE: __tsfieldinit__ would be an optimistic unique name// NOTE: __tsfieldinit__ is a placeholder. It won't actually be evaluated.static[(<inlinedexpressions>, "__tsfieldinit__")]() {}}<staticassignments>
A.__tsfieldinit__ = void 0; // mark undefined, but otherwise leave as is. `delete` is terrible for performance...
NOTE: we could possibly abuse name in this case..., though I'm not sure that's much better:
var<tempvariables>;
class A {
#x;static[(<inlinedexpressions>, "name")]() {}}Object.defineProperty(A,"name",{value: "A",writable: false});// fix up A.name<staticassignments>;
I like Rob Palmer (@robpalme)'s idea of ESNext implying useDefineForClassFields; the emit is simpler and spec-standard class properties are widely enough supported that people overusing target:esnext [1] are unlikely to run into problems.
The downside of this is that it could be considered silent breaking change (if you previously used --target:esnext on its own, your class field emit changes without notification, which can lead to subtle breaks). If we were going to make a change, I'd suggest that --target esnext should make you explicitly define your choice for --useDefineForClassFields if you use class fields in your code (assuming you haven't set --noEmit), at least for a few releases (or a future TS 5.x) to make people aware of the change.
We do have other cases where we downlevel things like destructuring to the ES5 form even when targeting ES2015, particularly in the module transforms, which is why destructuring.ts is separate from the es2015.ts transform. We could consider forcing the downlevel private field emit when we encounter the combination.
Activity
robpalme commentedon Dec 5, 2020
Thanks for highlighting this. I played around and reduced it to a slightly simpler case in the playground.
...produces the following incorrect ESnext emit...
...whereas desired ESnext emit should retain the static field initialization inside the class...
useDefineForClassFieldsbecause the static field initialization remains inside the class.Making the static field assignment work without
useDefineForClassFieldsmight be possible if we sneakily export a getter.But this feels like a lot of work for the matter at hand. Maybe the simplest solution would be to make
target:ESnextimplyuseDefineForClassFields:trueto side-step the problem entirely. Alternatively, an easy/quick solution to achieve correctness would be to degrade thetarget:ESnext && useDefineForClassFields:falsecase to use down-levelling.Until this is fixed, users of
target:ESNextcan workaround this by enablinguseDefineForClassFieldsa-tarasyuk commentedon Dec 7, 2020
Daniel Rosenwasser (@DanielRosenwasser) I suppose
staticmethods/properties should be transformed because theproposal is at Stage 3. Am I right?a-tarasyuk commentedon Dec 9, 2020
Why I asked this question, maybe it doesn't make sense, https://github.com/tc39/proposal-class-fields is at Stage 3 too, and TS emits Private fields without polyfills. Example
However, TS downgrades static methods/properties. Example
sandersn commentedon Jan 15, 2021
I like Rob Palmer (@robpalme)'s idea of ESNext implying useDefineForClassFields; the emit is simpler and spec-standard class properties are widely enough supported that people overusing target:esnext [1] are unlikely to run into problems.
For the explicit combination of (1) target:esnext (2) useDefineForClassFields: false (3) nested static class with private reference, we should add an error that says "you can't do this, change useDefineForClassFields: true, or target an earlier version of ES."
[1] for example, somebody who just wants to use every feature in JS without the compiler doing any downlevelling, but who hasn't thought too much about which browser will.
dragomirtitian commentedon Jan 22, 2021
Nathan Shively-Sanders (@sandersn) I would like to pick this up if that's ok. Just to clear on the statement of work:
target:esnext,thenuseDefineForClassFields: truewill now be the default.target:esnext, useDefineForClassFields: truewe emit correct code so no further action is needed (ex)target:esnext, useDefineForClassFields: falseand we encounter a usage of a#privatefield in a static member we emit an error like "You can't do this, change useDefineForClassFields: true, or target an earlier version of ES."Merge remote-tracking branch 'remotes/origin/master' into microsoftGH…
Merge remote-tracking branch 'remotes/origin/master' into microsoftGH…
rbuckton commentedon Mar 18, 2021
I would strongly discourage using
delete, as that is a performance cliff in V8 and other engines and implementers have strongly advised not to do this.One approach I've seen is to leverage a computed property name, if possible:
This, of course, doesn't work if the class has no non-private members, in which case we might have to add an unused member, as you propose, but don't delete it.
NOTE: we could possibly abuse
namein this case..., though I'm not sure that's much better:rbuckton commentedon Mar 18, 2021
The downside of this is that it could be considered silent breaking change (if you previously used
--target:esnexton its own, your class field emit changes without notification, which can lead to subtle breaks). If we were going to make a change, I'd suggest that--target esnextshould make you explicitly define your choice for--useDefineForClassFieldsif you use class fields in your code (assuming you haven't set--noEmit), at least for a few releases (or a future TS 5.x) to make people aware of the change.rbuckton commentedon Mar 18, 2021
We do have other cases where we downlevel things like destructuring to the ES5 form even when targeting ES2015, particularly in the module transforms, which is why
destructuring.tsis separate from thees2015.tstransform. We could consider forcing the downlevel private field emit when we encounter the combination.