Skip to main content
A private member in TypeScript is a class property, method, or accessor prefixed with the # symbol, implementing the ECMAScript standard for strict, runtime encapsulation. Unlike TypeScript’s private access modifier, which only enforces visibility during static type checking, # private identifiers provide “hard privacy” and are completely inaccessible outside of the declaring class’s lexical scope at runtime.

Technical Characteristics

  • Broad Applicability: The # syntax applies not just to fields, but also to class methods (#method() {}) and accessors (get #prop() {}).
  • Strict Lexical Scoping: The member is bound strictly to the class body where it is defined. It cannot be accessed by derived classes (subclasses) or external instances.
  • Runtime Inaccessibility: Private members cannot be bypassed using dynamic property access. While a TypeScript private property can be accessed at runtime using bracket notation (instance['propertyName']), attempting to access a private field via bracket notation (instance['#propertyName']) evaluates to undefined. It does not access the private field, nor does it throw an error; it simply searches for a standard public property with the literal string key "#propertyName".
  • Mandatory Upfront Declaration: You cannot dynamically add a private field to an instance. It must be explicitly declared in the class body.
  • Distinct Namespace: Private fields exist in a separate namespace from public properties. A class can simultaneously possess a public property and a private field with the same base identifier (e.g., value and #value).

Ergonomic Brand Checks (in Operator)

TypeScript supports ECMAScript ergonomic brand checks. The in operator can be used to safely determine if an object possesses a specific private field at runtime. This evaluates to a boolean without throwing a TypeError if the object is not an instance of the class.

# vs. private Keyword

TypeScript supports two mechanisms for class member privacy. Understanding their mechanical differences is critical:

Compilation and Target Environments

The emitted JavaScript for # private members depends heavily on the target specified in your tsconfig.json:
  • ES2022 and later: TypeScript emits the native ECMAScript # syntax directly, relying on the JavaScript engine’s native implementation.
  • ES2021 and earlier: TypeScript downlevels the private fields using a WeakMap implementation to guarantee runtime privacy without leaking memory.
Tired of Poor TypeScript Skills? Fix That With Deep Grasping!Learn More