-
Notifications
You must be signed in to change notification settings - Fork 113
What about protected methods, better declaration of private fields, private async functions, and destructuring? #59
Comments
|
I disagree with your first point. class A {
static prop = 123
method() {
new.target.prop // <-- Is it less confusing than this.#someProp?
A.prop // <-- Do devs wonder why not A.static.prop? or this.static.prop?
}
} That's mainly the reason. Devs are not dumb, that's my point. class A {
static prop = 1
static #prop = 1
#prop = 1
static method() {
return this.prop + this.#prop
}
other() {
// Btw, can we access new.target.#prop?
return new.target.prop + this.#prop
}
} Accessing static fields is already different and devs get it. There is no confusion why not using class A {
static prop = 1
#prop = 1
℃newKindOfProp = 1 // <- ℃? because it's another random char available?
} I get why class A {
private a = 0
method() { return this.#a }
} However, what this one tells you? class A {
#a = 0
method() { return this.#a }
} The code stops being auto descriptive, it confuses everybody at first sight, it's inconsistent with how At the end, even this alternative way is better: class A {
private #a = 0
private #method() {}
a = 0
method() {
return this.#a
}
} At least it's clear. It's not redundant to write |
class A {
private a = 0
} That will lead people to believe it's accessed by class A {
private #a = 0
} Will lead people to thinking they could use |
I buy your first argument against Your second argument is just wrong. Why? class A {
validProp = 0
$validProp = 0
_validProp = 0
#validProp = 0 // <- (I'd think) if method begins with $ and _, so # is also public, right?
#$validProp = 0
#_validProp = 0
#$#_validProp = 0 // <- Why people won't think that this is possible seeing previous declarations?
static $validProp = 0
static _validProp = 0
static #validProp = 0
} Could you clarify how this is less confusing or problematic? That's the reason why I think the hate of using Once again, the I don't like so much the idea of this proposal. However, I understand what it tries to solve and prioritizes library devs over consumers or users of libraries. So, a dev building a library is not a dummy learning JS, they will get what How wrong can something simple as this be: class A {
validProp = 0
$validProp = 0
_validProp = 0
private #validProp = 0
private #$validProp = 0
private #_validProp = 0
static $validProp = 0
static _validProp = 0
static private #validProp = 0
} The class A {
#privateField() {
// do your stuff
}
/* @deprecated */
_privateField() {
displayWarning('Accessing internal properties would be unavailable in future releases')
this.#privateField()
}
} That looks just like renaming my method. It isn't obvious what the code does. I would like to know what kind of "people", you refers to, that would be confused that much by adding a keyword that will tell a var is |
i guess i will continue to use Typescript, the compiler will translate the private -> to the # and what so ever other symbol they (TC39) will decide to use . Because for me : private, protected, public, static are more meaningful than #. |
@Davilink the private const obj = {
#prop = 0
get prop() {
return this.#prop
}
} That is also supposed to avoid collision with field names between public and private fields. Here tc39/proposal-decorators#33 (comment) I mentioned another idea I had for accessing private fields, such as: class {
static prop = 0 // static
private prop = 0 // private
prop = 0 // public
method() {
new.target.prop // refers to static field
new.self.prop // would refer to private field
this.prop // would refer to own
}
} That doesn't cause collision between fields with the same name. It also avoids introducing a new symbol ( |
This is not part of the current proposal, no. It's true that TypeScript's |
@bakkot I guess the docs are not updated everywhere. I guess that simplifies things. I would still prefer to access private fields in a different way.. or declare them in a different way. |
@eddyw Which doc still uses private in object literals? I was trying to clarify that this is not supported. About declaring and using private fields differently, isn't the # a difference? |
@littledan here https://github.com/tc39/proposal-decorators/blob/master/DETAILS.md The thing is that the class {
#color = '#aabbcc'
#smallNumberSign = '﹟'
#match = /\#([^#]+)$/g
#hash = location.hash
} Besides, it is not the first time the So, based on the idea of class {
private prop = 0
method() {
in.self.prop // private .. or
in.this.prop
}
} Why? It can't be used as a field name and ..
So a meta property there makes kind of sense. It's kind of This would also make possible other things. For instance, to suggest in the Reflect Metadata proposal (in the future) to add possibility to access metadata using class C {
@Reflect.metadata('key', 'value')
method() {
in.meta['key']
}
} Anyway, the thing is. This proposal struggles where to put |
Thanks for pointing at that file; I will update it to move private in object literals to the future work section. In general, you should only need to look in this repository to understand this proposal, but I think the repo needs a big "detailed rationale" blog post to tie it together. On my to do list. We have covered the two points here (# is ugly, and using metaproperties which skip this) in several other bug threads already. |
@littledan I couldn't really find a bug thread about metaproperties. I would really like to read about it. Were they covered somewhere in this repo? A Google search doesn't return anything useful. |
+1 for protected. +1 for using proper keywords instead of "#". If you are doing heavy subclassing, private alone is almost useless, so protected is needed. |
What about both? class Foo {
private #x = 1;
protected #y = 3;
get x() { return #x; }
set y(newVal) { #y = newVal; }
} Advantages:
Downsides:
|
@rattrayalex See previously and previously. |
Thanks! Sorry for the repost. |
Much of this was discussed previously in this thread (I realize it's very long). I think one of the best points that was brought up (by @bakkot) is that the best way to figure out good ways of doing access control in JS is to provide user-land features that the community can experiment with, and use those to inform future proposals. Hard private fields are an exception to this because there's no way to provide truly private fields with decorators or any other proposals currently on the table. If I understand correctly, the introduction of the
(These are just examples of course; naming, meaning, and feasibility of the specific access levels would need to be discussed.) |
@mbrowne I agree that this proposal does not preclude any of those. The only thing it does, due to the choice to have ASI, is say that those cannot have a newline between that token and the field or method it is modifying. This is already the syntax for async methods, so I don't think the restriction is so bad. |
@mbrowne |
Damned, "public", "private" and "protected" are already reserved keywords. What is the point to use a sigil for "private" to just break "protected" and potentially others like "friend" ? |
If you look at the suggestion I linked to before, you'd notice I want to use the already reserved words and the existing syntax to accomplish everything. Here's a summary of what I'm suggesting.
As for
To make use of such
In the
With only that much, protected members would be fully functional. |
@doodadjs Just a note... |
@rdking I don't know for "friend", but that's potentially something that can come up. And it has no link with "goto/gosub" in BASIC ? |
@doodadjs I don't really understand how internal would be a good idea as a keyword in ES, it doesn't do anything that makes sense for ES. Consider that languages supporting such a concept do so because they support things like packages for Java and assemblies for C#. For ES, the closest concept is a module, but every implementation of modules in Javascript already make the context inside the module private to the module and public to anything declared in the module. /*ExampleModule.js*/
var moduleCommon = 0;
export class A {
constructor() {
console.log(`moduleCommon: ${++moduleCommon}`);
}
}
export class B {
constructor() {
console.log(`moduleCommon: ${++moduleCommon}`);
}
}
/* ExampleTest.js */
import {A, B} from "ExampleModule";
new A(); // => 1
new B(); // => 2 So I'm not sure what there is to gain from an |
One possible use case for However, I understand the argument for |
I'm not saying that something like |
Well you would still need something in the code that the module system could pick up on in order to be able to mark some class properties as internal and others as globally public, private to the module, etc. I suppose decorators would be one option (it could be a decorator with an empty implementation since it wouldn't be implemented in userland)...or special comments. |
(fwiw, modules are an ES6 concept - everything is implemented outside of ES, but the spec does mandate the existence of, and semantics of, modules) |
@ljharb I just noticed that in the spec myself last night. There's still the issue that, given the usage suggested by @mbrowne, some concept of a "package" would have to be developed to limit the sharing of |
The use case I mentioned might not be the only one; it might also be useful to have access that's internal to a single module. In most cases that can be handled by simply not exporting things, but what if you want to make some properties or methods of an object public but not others? While you could create a proxy to wrap your object and only expose what you want, that might not always be desirable. However, I realize this is a bit of an edge case and not a priority for discussion at the moment - just wanted to raise it for future consideration.
|
So, I actually couldn't find any threads that cover or discuss why meta-properties are not a good option to get around the ugly syntax and, at the same time, allow future proposals to be considered (protected, friends, [and what not]). For instance: class A {
private prop = 123
method() {
in.self.prop = 234
in.this.prop = 234
}
} Or any other way of extending On the other hand, if There is a confusing thing as well. class A {
@private prop = 123 // let's pretend deco. fn. private exists
@friend prop = 123 // there's collision between names
#prop = 123 // no collision
} That means, even though hard private is supposed to have a unique name, for class A {
#prop = 123
private prop = 123
protected prop = 123
friend prop = 123
method() {
in.#prop // hard private
in.private.prop
in.protected.prop
}
} On the other side, I don't believe we really need all of those being implemented (e.g: protected, friend, package, etc). So, there is a reserved word for class A {
private prop = 123 // hard private
@private prop = 123 // your own implementation of soft private fields with fn decorators.
method() {
in.this.prop = 234 // hard private
}
} Anyways, just my thoughts. I'd really want to know if meta-properties were discussed before and if there is a thread about it. I couldn't find it for some reason.. (maybe it's in one of the many repos?) |
One thing I don't like about |
I share @adrianheine 's concern here. For reasons described in other threads, I'd rather not switch to @rdking 's |
@eddyw Meta-properties and similar proposals are discussed in tc39/proposal-private-fields#14, notably here. Briefly: you'd still need a new kind of declaration, I think, since |
I opened an issue in tc39/proposal-private-methods#21, however I just realized that further discussion about the topic should be here. So, I will put again what I wrote there:
How could we allow a
private
field to be shared only when subclassing but not when it's an instance? Why notprotected
fields were not proposed? Have you thought if in the future somebody does propose them? (we are running out of characters)I know it was discussed many times why
#
was chosen (I read the FAQs). It's ugly but I get it. However, that doesn't mean there cannot be another way of declaring them. What about this?:Declaring them that way makes more sense besides it doesn't break the current proposal's idea.
However, if somebody comes up with "this may be confusing..." or similar, devs do not seem confused using
static [prop]
, they know how to access a static var (new.target[propName]
). In a similar way,private [prop]
won't make it more confusing, devs will know to access withthis.#[prop]
.An example where it will look better:
What about async functions?
How will it behave with destructuring. For instance (with no Private fields):
With private fields:
Are private fields notgot it :Penumerable
?, I mean, is it possible to get all the names of private fields from inside a class? If they are not enumerable, how does it affect applying a function decorator and changing the descriptor toenumerable: true
?How does it act with
Proxy
?So far I have more questions that actually use cases since I'm not pretty sure how much does it affect everything we know about JS. Now we have
block scope
vars andfunction scope
vars. Maybe explaining how to categorizeprivate block/function scope
vars will clarify better all questions.The text was updated successfully, but these errors were encountered: