Repository navigation
The future of the "private" keyword #31670
Description
Activity
- addedDiscussionIssues which may not have code impactIssues which may not have code impact
on May 30, 2019 I would personally prefer to keep the
privatekeyword. To me, it looks waywayway better than#. And is more clear in its intent, to me. And I wouldn't want to have to go back and do a regex replace forprivateto#.When I found out
#was being proposed instead of justprivate, I thought I was experiencing the Mandela effect.Reacted by Joe Pea, Edoardo Luppi, floydjdx, Ashlynne Mitchell, Mike Cann, James, Glen, kktos, Sam Denty, gaowanqiu and 112 moreReacted by fritz, Umed Khudoiberdiev, SancheZz, 风痕, Kalashnikov Ilya, Danko Petrovic, Bogdan, Pavlo, Joe Pea, Akash Singh and 1 moreReacted by Nico Jansen, Jim Buck, Anton, AsukaSong, HE Shi-Jun, Roberto Malatesta, Sebastien Dubois, ExE Boss, Hunter Kohler, Joe Pea and 1 moreReacted by Joe Pea, Null Void, Chris Kuech, Emanuel Achirei, hasezoey, Roberto Malatesta, Ambar Mutha, gosua, Klemen Oslaj, Zoltán Ujszászi and 7 moreReacted by Ambar Mutha, Evgeniy OZ and Hunter KohlerRyanCavanaugh commented
on May 30, 2019 MemberMore actionsOur current plan is to leave the current
privatebehavior as-is.Reasons for this:
- We don't make breaking changes for no reason, and nothing external is forcing people to move off of compile-time
private - There are legitimate use cases for compile-only privacy, e.g.
privatefields can be read from unit tests (I realize some people find this personally distasteful, but this is not universal) - Lots of people don't like the
#syntax so why force it on them - Without
WeakMap(which not all runtimes have) there's no good equivalent downleveling for hard runtime privacy
As for a transformer, probably not? It's simple to replace these with a regex if you're motivated.
Reacted by Michael Loughry, Zhaofeng Miao, Rob Palmer, Gareth Jones, Anton, recordare, Andrii Dieiev, varHarrie, Chris Blossom, Mateusz Paprocki and 52 moreReacted by fritz, Lasse Rosenow, Nikolai Kulikov, Ekadagami, Nikolay Lanets, Maciej Kravchyk, Ollie Jones and Nigro SimoneReacted by AnyhowStep, Titian Cernicova-Dragomir, Gareth Jones, Joey Wunderlich, Max Heiber, recordare, Stefan Schult, Sean Vieira, Josh McDonald, Nick Breaton and 9 more- We don't make breaking changes for no reason, and nothing external is forcing people to move off of compile-time
RyanCavanaugh commented
on May 30, 2019 MemberMore actionsOh the other interpretation of "transformer" would be "Would TS emit
privateas#", the answer to which is definitely "no" because that would require type-directed emit.Reacted by Bruce Pascoe, Max Heiber, Gareth Jones, Aleksey Levenstein, Kliment Petrov, dkrysiak, Sandor, Garen Yondem, Zzzen, David B. and 4 moreReacted by Tim StackhouseReacted by iamartFWIW I don’t find the type-directed emit argument that strong in this case; it’s a stretch to call
private“type info” IMO (also we’re emitting the class itself, so we could just say it’s an alias for#and thus not really type info at all). That said, changing TS to emit private members as#would be a massive, massive breaking change (at runtime too!), so that’s plenty good reason to avoid going that route as far as I’m concerned.waywayway
I found this way funnier than I should have, I think.
Reacted by Mark, hasezoey, ems-ja, Garen Yondem, tipakA, James Bromwell, April Arcus, Eliaz Bobadilla, Keivan, Albert Mañosa and 2 moreWhen I found out # was being proposed instead of just private, I thought I was experiencing the Mandela effect.
On a related note, it’s good to know I’m not the only person who thought I had been dropped into Bizarro World when I first found out about the
#sigil.Reacted by Jim Buck, mihailik, Nick Clifford, kktos, Null Void, Aleksey Levenstein, Kaelan Cooter, rcollina, roman, John Farrell and 21 moreReacted by Frank TopelReacted by Roberto Malatesta and Chris RutkowskiRyanCavanaugh commented
on May 30, 2019 MemberMore actionsFWIW I don’t find the type-directed emit argument that strong in this case; it’s a stretch to call private “type info” IMO (also we’re emitting the class itself, so we could just say it’s an alias for # and thus not really type info at all).
Remember that private fields allow cross-instance access. Consider something like this:
class A { private y = 0; method(arg: A) { console.log(arg.y); } }
The "correct"
#-based emit of this would require type information onargin order to detect thatyshould be rewritten to#yReacted by Bruce Pascoe, Thomas Mur, Peter Burns, Caleb Eggensperger, acceptable, Roman, Eliaz Bobadilla, Andrei Cristea, Va Da, CoeJoder and 2 moreReacted by Va DaYeah, I see what you mean now, basically
obj.foois ambiguous without type info if there’s a possibility it can “really mean”obj.#foo. Thanks, hadn’t thought of the cross-instance case.Reacted by Max Heiber, Titian Cernicova-Dragomir, Nico Jansen, ems-ja and Evgeniy OZOther sometimes-advantages of OG
privateis thatprivatefields show up inJSON.stingifyoutput and can show up inconsole.log.Reacted by Titian Cernicova-Dragomir and Peter FlynnHi I was pointed to this thread by folks regarding the runtime performance concerns mentioned in #31670 (comment). (For some background, I implemented private fields in V8)
private fields have much better runtime perf, especially in downlevel scenarios
Can you expand on this? My understanding is that typescript private is downleved to public property access. AFAIK, the extra overhead for ES private is just one machine load for loading the
PrivateNamebacking the property when compared to public property access. I don't expect this to be of significant runtime overhead. Is there something else?Reacted by chocolateboy, DaviDevMod, Cris Perdue and Jerboas86A few more questions that weren't asked yet (it seems):
- Would #-fields allow modifiers like
readonly? - Will
public #x,private #xandprotected #xbe an error? - Will it be possible to assign #-fields from the constructor parameters, the same way as the current parameter-properties work (i.e.,
constructor (private x)andconstructor (#x))? - What visibility rules will apply between
privateentities and#-entities? What if I use a private-entity from a #-entity, or vice versa? Seems that #-entites are 'more' private than private-entities, in some sense. :)
Thanks.
Reacted by Nico Jansen, Titian Cernicova-Dragomir, Abraham White, Andrii Dieiev, elovin and Vadim Ovchinnikov- Would #-fields allow modifiers like
- Would it be possible to do this?
class Foo { bar: string; #baz: number; } const foo: Foo = { bar: 'bar' };
or is
#bazpart of the shape ofFoo? (hopenotso).RyanCavanaugh commented
on May 31, 2019 MemberMore actionsAFAIK, the extra overhead for ES private is just one machine load for loading the PrivateName backing the property when compared to public property access. I don't expect this to be of significant runtime overhead. Is there something else?
I over-extrapolated; I'll trust your assessment on this. I've updated the comment to be more accurate.
RyanCavanaugh commented
on May 31, 2019 MemberMore actionsWould #-fields allow modifiers like readonly?
Yes,
readonlyis meaningful inside the classWill public #x, private #x and protected #x be an error?
Yes
Will it be possible to assign #-fields from the constructor parameters, the same way as the current parameter-properties work (i.e., constructor (private x) and constructor (#x))?
No
What visibility rules will apply between private entities and #-entities? What if I use a private-entity from a #-entity, or vice versa? Seems that #-entites are 'more' private than private-entities, in some sense. :)
The visibility of a method doesn't change which properties it's allowed to access
Would it be possible to do this? (structurally skip a # member)
No. The given object is not a substitute for
Foo; the same reasoning about cross-member access requiringprivatemembers to be present applies hereReacted by Nico Jansen, Anton Lobov, Andrii Dieiev, dkrysiak, Roman and Vadim OvchinnikovThe given object is not a substitute for Foo; the same reasoning about cross-member access requiring private members to be present applies here
I’m not sure I understand the rationale of this; for TS-private this makes sense because of JS interop (the “private” property is actually public from JS POV); for
#fields, only the class itself can access it, even at runtime, and therefore as long as the rest of the shape matches, it should be fine from a duck-typing POV.Is there a case I’m missing where the above substitution wouldn’t be safe?
edit: Wait... I bet it’s the cross-instance case again, right?
Reacted by Nico Jansen, ems-ja, Stav Noy, Shotaro Nakamura and snarbles253 remaining items
It could still be nice to have an option to make
privatecompile to some sort of soft run-time privacy though. (So not#, but maybe symbols...)I believe that's out of scope of TypeScript's goals (essentially the goal is to avoid producing new runtime language features, and make only strippable type syntax on top of standard JavaScript), but compiling
privateto soft privacy using symbols, under an option, would in fact be pretty sweet.2025! the private visibility modifier can be dangerous on some case, eg : doing Stringify object, the private modifier can be exposed on JSON.
Reacted by irfanstractReacted by MatthiasReacted by Ryan CavanaughThere's nothing gained but confusion by adding a flag to conflate the two.
There is something gained. The #-fields can be shortened easily, similar to how function, class and variable names are minified. Tools like Terser replace long #-names with short garbage. This gives 2 advantages:
- Better concealing of the source code.
- Better code size deduction from minification.
Do you have other options for achieving these goals? Replacing all
privatefields with #-fields in the source code is a solution, but it's awkward for multiple reasons. Havingtscdo a prepending of#to the private fields is such a simple and elegant solution, that I'm surprised it hasn't been implemented yet. I call it simple, because it can be done in a file using just the content of that file. That is, it doesn't violateisolatedModules.because that would require type-directed emit
Why is it allowed for
const enum? It's also an elegant solution for source code concealing and code size deduction. What's the key difference?Having tsc do a prepending of # to the private fields is such a simple and elegant solution, that I'm surprised it hasn't been implemented yet.
It really, really isn't that simple.
privateand#have different semantics. Transpiling the former to the latter can break things in all kinds of ways.You would have to change the meaning of
private, but the (subjective) upsides of having a "soft-private" and backwards compatibility concerns make for a rather strong argument against doing so, to put it mildly.You could argue for having a compiler switch, but if people find it confusing that
#andprivatemean similar but not identical things, you're not doing the world favors by adding into the mix thatprivatecould mean two different things depending on compiler version or settings.Why is it allowed for const enum? It's also an elegant solution for source code concealing and code size deduction. What's the key difference?
It is grandfathered in, a vestige from before TypeScript adopted the position against type-directed emit.
Reacted by Holger Jeromin, Ryan Cavanaugh and ExE BossReacted by Sergey M.the private visibility modifier can be dangerous on some case
so we should start warning against
privateand call for rewrite.private lastChgd: number; + ^^^^^^^ + classic 'private'. consider migrating to ES Private Fields.
abstract class LivingDocument { private lastChgd: number; ... }
Why is it allowed for
const enum?certain transpiler(s) drop
constand treat them like plainenummaybe?RyanCavanaugh commented
on Aug 17, 2026 MemberMore actionsYou can add a lint rule if you don't want to use
private. We're not going to break huge amounts of code for a "warning" that doesn't do anything.What about a tsconfig option that removes it entirely?
Why would you want to remove it? It has well understood semantics that can be useful. I'm not sure any of the considerations that factored into to the current state of affairs have changed meaningfully.
RyanCavanaugh commented
on Aug 17, 2026 MemberMore actionsWhy have a flag for banning
privatevs any other syntactic feature you might not like? I don't get the distinctionWhy have a flag for banning
privatevs any other syntactic feature you might not like? I don't get the distinctionWhy have a flag for banning
anythen?
privatearguably does more real-world harm thanany- given private isn't actually private and the corpus of code written by neophytes with wrong expectations about it.Reacted by Jordan HarbandRyan Cavanaugh (@RyanCavanaugh) there's a large qualitative difference between banning a type-space syntax and banning a value-space syntax, especially one that will never be standard, and whose emit provides false encapsulation.
snarbles2 none of its semantics are useful - if you want truly private data, use JS private fields; if you don't, just use symbols or strings (like, a leading underscore, or whatever).
privateis no different than the latter.Certainly not true. There are things you can do with
privatethat you can't with#private, andprivateis still compiler enforced. This even comes up in vanilla JS, where you have to resort to _prefixes.You can use symbols for that purpose, I suppose, but I have a hard time buying that I should sacrifice ergonomics/readability to satisfy somebody else's notion of what's "right".
I'm usually the last person to make this suggestion, but a linter is the right solution here.
One thing that might help would be to say that the
privatekeyword is deprecated in the official docs. Obviously it won't ever be removed because of the need to preserve backward compatibility, but it's non-standard and IMO it would make sense to officially deprecate it given native private and other options available.Reacted by Jordan Harband and rjgottenThere are things you can do with
privatethat you can't with#privateAnd you can only do those things because
privateis misnamed and actually at runtime is anything-but.I'm usually the last person to make this suggestion, but a linter is the right solution here.
An option in the compiler to disallow
private- pretty much the same as the one that disallowsany- for all intents and purposes is a linter option.You can use symbols for that purpose, I suppose, but I have a hard time buying that I should sacrifice ergonomics/readability to satisfy somebody else's notion of what's "right".
I would be all for TypeScript inventing a syntax for symbol usage that could desugar properly without runtime surface as well as make
unique symboleasier to stomach within the typing system. Because woo-boy is that a pain in the dot-dot-dot to work with...Reacted by Jordan Harband and Matt Browne
This is a somewhat "catch-all" issue to serve as a place of discussion for a question that is sure to come up given the advancement of Private-Named Instance Fields:
What is the future of the "private" keyword in TypeScript?
That's the general question - following are some short-and-sweet Q&As that I've pulled up for visibility.
Is TypeScript going to depreciate it, and aim towards phasing it out in favor of the private field operator?
Is it going to be supported in the form of a transformer , turning
private barinto#bar?A few more questions:
Would #-fields allow modifiers like readonly?
Will public #x, private #x and protected #x be an error?
Will it be possible to assign #-fields from the constructor parameters, the same way as the current parameter-properties work (i.e., constructor (private x) and constructor (#x))?
What visibility rules will apply between private entities and #-entities? What if I use a private-entity from a #-entity, or vice versa? Seems that #-entites are 'more' private than private-entities, in some sense. :)
Would it be possible to do the following: