Hard-private vs soft-private · Issue #33 · tc39/proposal-private-fields (original) (raw)
Should private state have an "escape hatch" like TypeScript private, or be strongly private the way that closed-over values are? Soft-private isn't fully written out the way that @zenparsing has done an excellent job on the current WeakMap-based proposal, but the core would be that "private state" is simply (public) symbol-named properties, with syntactic sugar for those symbols, and possibly some kind of introspection over them. It might be bad for overall language complexity/intelligibility to have both kinds of private state in the language and better to decide up-front what we want. Below, I @-mention people who either made this argument or who I think might be especially interested in the argument.
Soft-private
Soft-private would be syntactic sugar for symbols. In ES6, you can currently write code like this:
let x = Symbol("x"); class Foo { constructor() { this[x] = 1; } }
With a soft-private language feature, there would be syntactic sugar that looks like this:
Advantages
- Possibly easier solutions for friends, decorators introducing private state, etc., since it's just a matter of sharing the symbol somehow (@wycats). Introspection method TBD.
- One might argue that JavaScript is already so flexible and so much can be subverted that there's little value in trying to give guarantees to private state (@bterlson).
- From a testing perspective, if tests can reach into private state, then the main code does not need anything extra written inline which should properly be only in the test code (@mhevery).
- This is a language feature that is explained/polyfillable in terms of other language features, and doesn't create any new sort of different properties that behave differently. Therefore it's a simpler conceptual model (@justinfagnani, @ljharb).
Disadvantages
- Private state is not really guaranteed private, so no hard guarantees about properties that users may expect if they want security guarantees about how their objects are used (@erights).
- Platform objects, both within ECMAScript and in embedding environments, contain hard private state. If a library wants to be high-fidelity and just like a platform object, soft-private state does not provide this (@domenic)
Hard-private
Hard-private (the proposal described in this repository currently) is based on state that can really only be accessed by code within the class body. This has been constant across all of the different proposals that have been advanced to TC39, though not what TypeScript provides.
Advantages
- Hard-private could provide actual guarantees about visibility and more assurances to library authors that their internal state will not be accessed inappropriately, allowing them to build something more like builtin internal slots. (@domenic, @erights)
- With
static
blocks that run as part of class definition (currently not concretely proposed or championed), it would actually be possible to build getter/setter functions to share with friends (though this code may be slower, especially as the page is starting up), e.g.,
let get, set; class Foo { #bar static { get = x => x.#bar; set = (x, y) => x.#bar = y; } }
Even without static blocks, you could create a static method which does the exporting, though this is ugly.
- It might be possible for implementations to do certain kinds of reasoning about private state that they cannot do about Symbols (though the extent is limited by the dynamism of the current proposal) (@sebmarkbage).
- This is something which, to be efficient, probably has to be built into the language, unlike soft-private which can be done by a transpiler like TypeScript. If there are important use cases, then it might make sense to use our limited syntactic space for something that has to be a primitive, rather than something that a user-level transpiler could use, in the spirit of asking browsers to do what is most important to do in the browser (not that this is a goal that TC39 has agreed on). (may be interested: @wycats)
Disadvantages
- This is a new language construct, introducing something that's sort of like properties but not quite. That's extra complexity. (@justinfagnani)
- For decorator integration, we'd need a separate kind of way to declare these private properties, and a special way to refer to them in the added method bodies. Unclear how that would work exactly. Not nearly as flexible as simple symbols, which wouldn't really need much of any special support to add as decorators. (@wycats)
- If you have a program which declares private state, but you want to reach into the private state from outside either for testing or because you want to consider the private state part of a semi-public interface for mainline code, you need to insert an unergonomic escape hatch in explicitly, rather than just apply the escape hatch the code which uses the private state. (@mhevery)
- Public properties might be more important than private properties; that's what people use in JS today. It might not be worth it to create all this extra semantic stuff just for private state (@esprehn, @jeffmo).
Thoughts?