"Four ways to say 'this is mine.' Three of them are TypeScript-only; one is JavaScript."
The four modifiers
public(default): accessible from anywhere. You rarely write this explicitly because it's the default.protected: accessible from this class and its subclasses, not from outside.private(TypeScript): TypeScript-only privacy — compile-time enforced. At runtime, the property is a regular property accessible viaobj['name'].#private(JavaScript): true private syntax. Runtime-enforced via hidden slots. Attempts to access from outside are SyntaxErrors at parse time or TypeErrors at runtime.
Which to use
For new TypeScript code: prefer #private for truly private fields, especially anything sensitive (auth tokens, internal handles, raw memory). It can't be bypassed.
For existing code: private is fine — the compile-time contract catches most legitimate access mistakes. Migrating to #private is mechanical when you want stronger guarantees.
For inheritance chains: use protected for fields/methods that subclasses need but external callers shouldn't see. This is a common pattern in framework base classes.
The defaults that work: Most fields should be
private (or #private). Most methods should be public. protected is the rare middle ground. Public-by-default-everywhere is too leaky; private-by-default-everywhere is too restrictive.