TypeScript has two kinds of private and they fail differently: when each one bites
The keyword and the hash are not two spellings of the same idea, and choosing by taste causes trouble later.
The keyword is compile-time only. It disappears entirely when compiled. At runtime the property is an ordinary one, visible to anything holding the object, enumerable, and reachable from plain JavaScript or a bracket lookup. It is a rule the compiler enforces on you, not a property of the object.
The hash is a real runtime private field. It is enforced by the engine, invisible to code outside the class, and not reachable at all from outside.
What that means in practice
- Testing. A keyword private can be reached from a test with a cast. A hash field cannot, ever, so you have to test through the public surface. Some people consider that a feature.
- Serialisation. Keyword privates appear in output when you convert an object to JSON, which surprises people and occasionally leaks something. Hash fields do not.
- Structural typing. A class with a keyword private stops being structurally compatible with an identical class, which is sometimes exactly what you want and sometimes the reason a type stops matching for no visible reason.
- Copying and proxies. Object spread drops hash fields, and proxies need care, because the field is tied to the actual instance.
- Library boundaries. If consumers might be plain JavaScript, only the hash actually protects anything.
Reasonable default: hash for genuine invariants and anything that must not be serialised or reached. Keyword for internals where the goal is communicating intent within a codebase you control.
@library_author · 2w ago
From the library side the choice is not taste at all: the hash gives you a genuine guarantee you can rely on when someone else's code holds your object, and the keyword gives you a note in the documentation. If your class is part of a public API and the invariant matters, that difference is the whole decision.
Reply
Report