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
I think it would be nice to have the stage 1 private fields proposal implemented in TypeScript. It'll mostly supercede the current pseudo-private implementation, and a fully-correct implementation is only transpliable to ES6 using per-class WeakMaps. I'll note that the spec itself uses WeakMaps internally to model the private state, which may help in implementing the proposal.
Currently, the proposal only includes private properties, but methods are likely to follow. Also, the most important part of the proposal is that private properties are only available within the class itself.
What does the TypeScript team think about the syntax and the use of a symbol/character prefix?
I personally think that it looks awful, but apparently there are technical limitations (the usages need to identify the visibility, for reasons that I don't fully comprehend).
It would be sad to see TypeScript losing the current private/protected syntax, which is much cleaner and consistent with a number of other languages. Is this likely to happen? Are there any alternatives?
To add to this, there are talks about possibly switching the # and @ symbols around (i.e. using # for decorators), which would obviously have an affect on TypeScript as well.
Reacted by Gábor IMRE, Daniel Schuba, 阿尔贝鲁, Anton Amelekhin, Gy, Benjamin Strauß, Josiah Savary, Michael Theriot, Ivan Drinchev, Marek Lukáš and 27 more
I think it is "awful" too. I think like the :: bind operator, which didn't make it actually very far, it is worth holding off even trying to implement it until it gets a bit further down the path with T39. I highly suspect it will be a bit of a rough ride. Also, the down-emit to support it will be rather difficult I suspect, because of the need to rewrite every access site.
the usages need to identify the visibility, for reasons that I don't fully comprehend
Mainly because you need to identify at the usage site how to lookup the property, since it needs a different lookup algorithm so that descendent classes can re-use the same private names without needing to "know" about the ancestor private properties.
Reacted by Klemen Oslaj, Kaelan Cooter, 雪狼 and ems-ja
One advantage of the proposed syntax is that you can omit the this and just use #field directly. Further, the sigil may be swapped with decorators (tc39/proposal-private-fields#32) and instead @ would be used, so you'd use @field, just like Ruby. Do you find this syntax ugly still?
I don't see a good way to do that while supporting general eval as JavaScript does--a concrete token allows some differentiation and makes it possible to support private state that's not based on a static type system as in TypeScript. We've discussed a number of syntax alternatives at tc39/proposal-private-fields#14 , and I don't see TypeScript's current syntax as something that can work properly all the time in a fully general JS environment.
Mainly because you need to identify at the usage site how to lookup the property, since it needs a different lookup algorithm so that descendent classes can re-use the same private names without needing to "know" about the ancestor private properties.
I wish I knew more about how JavaScript works internally. I thought that it would work the way that it's described here and here, but apparently not. I still find it difficult to believe that that would have a significant impact on performance, but there are no numbers for comparison.
Do you find this syntax ugly still?
Essentially, yes. More so, I find it both inconsistent with the rest of the JavaScript syntax, as well as inconsistent with many other languages:
Language
Syntax
Notes
C#
private int x;
C++
private: int x;
Java
private int x;
PHP
private $x;
Python
__x
"no comment"
Ruby
private def method # ... end
It's a method, but fields are private by default I think.
TypeScript
private x;
Visual Basic
Private x As Integer
All but one of the above languages use the private keyword, and it seems that Python doesn't really have "proper" private state anyway.
With the exception of Python (again), none of the languages format field declarations or field accesses differently depending on the visibility, and they all allow for new visibility modifiers if that is ever required.
I also feel that both # and @ work better for things like decorators, that are (in Kevin's words) "syntactically 'outside' of the imperative flow".
I don't see a good way to do that while supporting general eval as JavaScript does
It would be nice to have a list of these issues with examples in one place, so that there is something to refer to.
Reacted by Gábor IMRE, Igor Bezkrovnyi, Benjamin Strauß, Karol Wąsik, Michael Theriot, Jerry.Z, Ger Hobbelt, yan5, Ivan Drinchev, Vlad Balin and 19 more
Reacted by Abbas Monfared, Ronan Connolly and msn0
I still find it difficult to believe that that would have a significant impact on performance, but there are no numbers for comparison.
It would, if you want it to be really private versus just a convention like it is in TypeScript. Whenever you look up a property in JavaScript, the process is essentially like this:
Look at instance for own property.
If no own property, look at prototype for own property.
Ascend prototype chain until no property found.
If found deal with property descriptor (writable, get, set, configurable)
If not found and read, return undefined or write, set value as own property if not sealed or frozen.
Now with privates with no pattern in their name, you wouldn't have own properties, you would have something like own private properties which wouldn't ascend a prototype chain. You would then have to look for them there as well for every operation that could possible access the private properties, because you can't be sure which one is being referred to. Looking it up in the normal way and then only if it is not found looking in privates could be very dangerous, because what if you found something in the prototype chain, but there is also a private version?
The biggest challenge is that JavaScript classes are still a bit of a misnomer. They are essentially syntactic sugar for prototypical inheritance constructor functions. There is one thought that occured to me which I will try to add to tc39/proposal-private-fields#14.
Would it be possible to explain this issue in layman's terms?
If it requires re-writing the call site, then you would never be able to support things like:
class Foo {
private foobar: string;
baz() {
const p = 'foo' + 'bar';
console.log(this[p]);
}
}
Or any usage of eval like structures. You could choose not to support things like index access for privates or no eval support, but then "why bother". Pretty much all of this has been pointed out in one way or another in the proposal there but people still are "but I like private" 😁.
you wouldn't have own properties, you would have something like own private properties
If you had a single set of properties with a visibility item in the descriptor, I guess the issue is that, if the property did not exist on the current object, you wouldn't know whether or not to ascend the prototype chain.
If it requires re-writing the call site, then you would never be able to support things like ...
Okay, I think I understand the eval/rewrite case now. Usage sites within the class would be rewritten to indicate the visibility based on the declared properties, and that wouldn't be possible unless they are simple lookups.
If you had a single set of properties with a visibility item in the descriptor, I guess the issue is that, if the property did not exist on the current object, you wouldn't know whether or not to ascend the prototype chain.
Hmm. But if it doesn't exist in the current class, then isn't it by definition non-private? If so, it would have to move up the prototype chain anyway (as with regular public property lookups). There would be no additional work for private access.
But if it doesn't exist in the current class, then isn't it by definition non-private? If so, it would have to move up the prototype chain anyway (as with regular public property lookups).
No, you don't want to ascend. Private labels aren't inherited and subclasses can re-use privates without worrying about masking/name collisions.
Glen (@glen-84) Both of those posts that you mentioned indicate problems with un-sigil'd private state. Do you have ideas for solutions to those problems? I think complicating the lookup chain would be risky compatibility-wise, make JavaScript harder to implement with good performance (possibly making existing programs slower), be basically impossible to square with Proxies, and generally, significantly complicate the mental model of the language (which already has a relatively complex object system).
In the cross-language comparison, you mention Ruby. I think Ruby is a good example of private state with a sigil--@. You can call getter and setter methods without a sigil.
There's a very limited set of syntactic things you can do with a Babel plugin; basically the parser has to already support it.
True. But in the interest of comparing with TypeScript, in Babel you can fork just the parser and set up your babel.config.js to use the custom parser and any custom syntax plugins you create. Yes it's a lot of work (especially learning how to add new syntax for the first time), but I'm not aware of any equivalent option for TS, and of course in Babel it's much easier to write transformation plugins based on existing syntax. I'm not sure what the current state of customization options is in TypeScript, but when I last looked into it a couple years ago it looked like there was no option to extend it other than forking the entire thing.
The hash for privates IMO is ugly and confusing. It’s hard enforced because there’s no way to implement them otherwise. There’s no build step like there is in TS. The effect is it just clogs up code with symbols for the very little gain of a “hard private” with people thinking it’s the ‘correct’ way to write JS as many people are taught that things should just be private by default.
There should have been more proposal before pushing it into the standard, especially when there’s been obvious disagreement on the matter.
robot56 things should just be private by default. They're taught that all things should be accessible via reflection, which is not, in fact, a good thing for anyone.
Jordan Harband (@ljharb) Yes, they should. However when the way to declare a private becomes throwing hash symbols in front of variables, the “correct” way to make a class in JS means teaching people to just putting hashes in front of member variables. Not just when declaring them, but also when referencing them. It’s especially confusing if you’re coming from other languages. My first thought when seeing something like this.#x = this.#something(); and declaration in a class was that the hash was apart of the variable itself. I would have never guessed it was a modifier. The underscore for public, that seems backwards too. This isn’t really relevant to TS, but just an annoying rant I guess.
Yes, learning new things when one is cemented in familiarity might be an adjustment. I have faith that the JS community will continue learning!
The way you put it, sounds like if that was learning something adding to our programming wisdom, whereas this is just another quirk of JS that one has to memorize :D
Reacted by Jordan Harband, Sergey and Dino Bettini
Reacted by Obed, Mark Penner, Daniel Santiago, Ghabriel Nunes, Maciej Sikorski, David Coxon, BlackGlory, Adil Hanney, Richard Moore, Egor Pronin and 2 more
Any update on this? I'm so used to private _fieldOrMethod (AS3, C#, Dart) that I have been ignoring the #fieldOrMethod syntax.
Being able to set an option private fields/methods to # in tsconfig.json would be really nice.
This was discussed and explicitly rejected at #31670. I don't think anything has moved in the interim that would cause us to revisit that decision.
It would have been nicer if you kindly pointed me to a solution, that I had to find myself after reading some of the replies to that thread, instead of the rude and implied "we decided NOT, so deal with it" (yes, I read your other responses).
My concern was that when transpiled, my private members would be left exposed, but the solution is, that TS transpiles the private members to WeakMap, so, not as private as with the #, but at least the members will have some kind of privacy in JSland.
Didn't know about that WeakMap trick, so your link at least provided me with a solution, and, for that I'm thankful. 🤯🔥
Just to clarify, I'm here (on GitHub) to give provide information about TypeScript feature prioritization, not JavaScript runtime behavior. If you're looking for information about how to write code with certain behavior in JS, Discord, StackOverflow, etc, are all available and encouraged, and indeed you'll see me helping out there from time to time. But I don't have time to offer 1:1 JS support on years-old closed issues in all cases and expectations should be set accordingly.
Activity
glen-84 commentedon Aug 14, 2016
What does the TypeScript team think about the syntax and the use of a symbol/character prefix?
I personally think that it looks awful, but apparently there are technical limitations (the usages need to identify the visibility, for reasons that I don't fully comprehend).
It would be sad to see TypeScript losing the current private/protected syntax, which is much cleaner and consistent with a number of other languages. Is this likely to happen? Are there any alternatives?
To add to this, there are talks about possibly switching the
#and@symbols around (i.e. using#for decorators), which would obviously have an affect on TypeScript as well.kitsonk commentedon Aug 15, 2016
I think it is "awful" too. I think like the
::bind operator, which didn't make it actually very far, it is worth holding off even trying to implement it until it gets a bit further down the path with T39. I highly suspect it will be a bit of a rough ride. Also, the down-emit to support it will be rather difficult I suspect, because of the need to rewrite every access site.Mainly because you need to identify at the usage site how to lookup the property, since it needs a different lookup algorithm so that descendent classes can re-use the same private names without needing to "know" about the ancestor private properties.
littledan commentedon Aug 17, 2016
One advantage of the proposed syntax is that you can omit the
thisand just use#fielddirectly. Further, the sigil may be swapped with decorators (tc39/proposal-private-fields#32) and instead@would be used, so you'd use@field, just like Ruby. Do you find this syntax ugly still?dalexander01 commentedon Aug 17, 2016
I don't think people will be happy with a non "private" keyword based solution.
littledan commentedon Aug 17, 2016
I don't see a good way to do that while supporting general
evalas JavaScript does--a concrete token allows some differentiation and makes it possible to support private state that's not based on a static type system as in TypeScript. We've discussed a number of syntax alternatives at tc39/proposal-private-fields#14 , and I don't see TypeScript's current syntax as something that can work properly all the time in a fully general JS environment.glen-84 commentedon Aug 20, 2016
I wish I knew more about how JavaScript works internally. I thought that it would work the way that it's described here and here, but apparently not. I still find it difficult to believe that that would have a significant impact on performance, but there are no numbers for comparison.
Essentially, yes. More so, I find it both inconsistent with the rest of the JavaScript syntax, as well as inconsistent with many other languages:
int x;
def method
# ...
end
by default I think.
All but one of the above languages use the
privatekeyword, and it seems that Python doesn't really have "proper" private state anyway.With the exception of Python (again), none of the languages format field declarations or field accesses differently depending on the visibility, and they all allow for new visibility modifiers if that is ever required.
I also feel that both
#and@work better for things like decorators, that are (in Kevin's words) "syntactically 'outside' of the imperative flow".Would it be possible to explain this issue in layman's terms? (maybe in tc39/proposal-private-fields#14)
It would be nice to have a list of these issues with examples in one place, so that there is something to refer to.
kitsonk commentedon Aug 21, 2016
It would, if you want it to be really private versus just a convention like it is in TypeScript. Whenever you look up a property in JavaScript, the process is essentially like this:
undefinedor write, set value as own property if not sealed or frozen.Now with privates with no pattern in their name, you wouldn't have own properties, you would have something like own private properties which wouldn't ascend a prototype chain. You would then have to look for them there as well for every operation that could possible access the private properties, because you can't be sure which one is being referred to. Looking it up in the normal way and then only if it is not found looking in privates could be very dangerous, because what if you found something in the prototype chain, but there is also a private version?
The biggest challenge is that JavaScript classes are still a bit of a misnomer. They are essentially syntactic sugar for prototypical inheritance constructor functions. There is one thought that occured to me which I will try to add to tc39/proposal-private-fields#14.
If it requires re-writing the call site, then you would never be able to support things like:
Or any usage of
evallike structures. You could choose not to support things like index access for privates or no eval support, but then "why bother". Pretty much all of this has been pointed out in one way or another in the proposal there but people still are "but I like private" 😁.glen-84 commentedon Aug 21, 2016
If you had a single set of properties with a
visibilityitem in the descriptor, I guess the issue is that, if the property did not exist on the current object, you wouldn't know whether or not to ascend the prototype chain.AFAIK, that won't be supported anyway (ref).
I still don't really follow the eval case. I thought that the property checks would happen at runtime, after property names had been calculated.
I'm trying not to be one of those people, and to understand the issues, but the proposed syntax just makes me uncomfortable. =)
glen-84 commentedon Aug 21, 2016
Okay, I think I understand the eval/rewrite case now. Usage sites within the class would be rewritten to indicate the visibility based on the declared properties, and that wouldn't be possible unless they are simple lookups.
glen-84 commentedon Aug 21, 2016
Hmm. But if it doesn't exist in the current class, then isn't it by definition non-private? If so, it would have to move up the prototype chain anyway (as with regular public property lookups). There would be no additional work for
privateaccess.(I'm possibly missing something obvious)
kitsonk commentedon Aug 21, 2016
No, you don't want to ascend. Private labels aren't inherited and subclasses can re-use privates without worrying about masking/name collisions.
littledan commentedon Aug 21, 2016
Glen (@glen-84) Both of those posts that you mentioned indicate problems with un-sigil'd private state. Do you have ideas for solutions to those problems? I think complicating the lookup chain would be risky compatibility-wise, make JavaScript harder to implement with good performance (possibly making existing programs slower), be basically impossible to square with Proxies, and generally, significantly complicate the mental model of the language (which already has a relatively complex object system).
In the cross-language comparison, you mention Ruby. I think Ruby is a good example of private state with a sigil--
@. You can call getter and setter methods without a sigil.glen-84 commentedon Aug 21, 2016
I meant move up if the property wasn't on the current class, to look for a public or protected property.
125 remaining items
arthur5005 commentedon Apr 1, 2020
mbrowne commentedon Apr 2, 2020
Ryan Cavanaugh (@RyanCavanaugh)
True. But in the interest of comparing with TypeScript, in Babel you can fork just the parser and set up your
babel.config.jsto use the custom parser and any custom syntax plugins you create. Yes it's a lot of work (especially learning how to add new syntax for the first time), but I'm not aware of any equivalent option for TS, and of course in Babel it's much easier to write transformation plugins based on existing syntax. I'm not sure what the current state of customization options is in TypeScript, but when I last looked into it a couple years ago it looked like there was no option to extend it other than forking the entire thing.Shinigami92 commentedon Apr 2, 2020
Try this: https://github.com/dsherret/ts-morph
robot56 commentedon Aug 4, 2020
The hash for privates IMO is ugly and confusing. It’s hard enforced because there’s no way to implement them otherwise. There’s no build step like there is in TS. The effect is it just clogs up code with symbols for the very little gain of a “hard private” with people thinking it’s the ‘correct’ way to write JS as many people are taught that things should just be private by default.
There should have been more proposal before pushing it into the standard, especially when there’s been obvious disagreement on the matter.
ljharb commentedon Aug 4, 2020
robot56 things should just be private by default. They're taught that all things should be accessible via reflection, which is not, in fact, a good thing for anyone.
robot56 commentedon Aug 4, 2020
Jordan Harband (@ljharb) Yes, they should. However when the way to declare a private becomes throwing hash symbols in front of variables, the “correct” way to make a class in JS means teaching people to just putting hashes in front of member variables. Not just when declaring them, but also when referencing them. It’s especially confusing if you’re coming from other languages. My first thought when seeing something like
this.#x = this.#something();and declaration in a class was that the hash was apart of the variable itself. I would have never guessed it was a modifier. The underscore for public, that seems backwards too. This isn’t really relevant to TS, but just an annoying rant I guess.ljharb commentedon Aug 4, 2020
Yes, learning new things when one is cemented in familiarity might be an adjustment. I have faith that the JS community will continue learning!
(the hash is part of the variable itself, the name of
this.#xis not "x, but private", but in fact,#x, a unique value in that lexical scope)TeoTN commentedon Sep 16, 2020
The way you put it, sounds like if that was learning something adding to our programming wisdom, whereas this is just another quirk of JS that one has to memorize :D
Neo-Ciber94 commentedon Jan 17, 2022
Would be nice to have a flag to make the compiler emit #private fields
Which simply converts this
To this
angelhdzmultimedia commentedon Apr 18, 2023
Any update on this? I'm so used to
private _fieldOrMethod(AS3, C#, Dart) that I have been ignoring the#fieldOrMethodsyntax.Being able to set an option
private fields/methods to #in tsconfig.json would be really nice.Thank you for all the nice job being done.
RyanCavanaugh commentedon Apr 19, 2023
This was discussed and explicitly rejected at #31670. I don't think anything has moved in the interim that would cause us to revisit that decision.
angelhdzmultimedia commentedon Apr 19, 2023
It would have been nicer if you kindly pointed me to a solution, that I had to find myself after reading some of the replies to that thread, instead of the rude and implied "we decided NOT, so deal with it" (yes, I read your other responses).
My concern was that when transpiled, my private members would be left exposed, but the solution is, that TS transpiles the private members to WeakMap, so, not as private as with the
#, but at least the members will have some kind of privacy in JSland.Didn't know about that WeakMap trick, so your link at least provided me with a solution, and, for that I'm thankful. 🤯🔥
Have a nice rest of the day, wish you health.
ljharb commentedon Apr 19, 2023
AngelHdz (@angelhdzmultimedia) a closed-over WeakMap is precisely as private as native private fields; that was part of the proposal design.
angelhdzmultimedia commentedon Apr 19, 2023
Awesome discovery! Thank you! Now I am relieved.
RyanCavanaugh commentedon Apr 19, 2023
Just to clarify, I'm here (on GitHub) to give provide information about TypeScript feature prioritization, not JavaScript runtime behavior. If you're looking for information about how to write code with certain behavior in JS, Discord, StackOverflow, etc, are all available and encouraged, and indeed you'll see me helping out there from time to time. But I don't have time to offer 1:1 JS support on years-old closed issues in all cases and expectations should be set accordingly.