ProgrammingFullstack Developer

How does the Private Fields mechanism work in TypeScript classes? What are the differences between private properties using the private modifier and using the # symbol, what are their application features and limitations?

Pass interviews with Hintsage AI assistant

Answer.

Background

In TypeScript, private properties were initially implemented using the private modifier, and starting from ECMAScript 2021, JavaScript supports true private fields with the # symbol. The main goal is data encapsulation to protect the internal details of the class from external access.

Problem

The private modifier in TypeScript provides privacy only at the compile time, but in the compiled JavaScript, these fields remain accessible through direct access to object properties. This can lead to unintended modifications of the object state. ES Private Fields (#) are completely inaccessible from outside the class even at the runtime level of JS, providing real data protection.

Solution

TypeScript supports both approaches. When choosing between them, consider the required level of protection and compatibility constraints with JS versions.

Code example:

class Example { private hidden: number; #trulyHidden: string; constructor() { this.hidden = 42; this.#trulyHidden = 'secret'; } getHidden() { return this.hidden; } getTrulyHidden() { return this.#trulyHidden; } } const x = new Example(); // x.hidden — error in TS, but will work via x['hidden'] in JS // x.#trulyHidden — syntax error even in JS

Key features:

  • private guarantees privacy only at the compile stage of TypeScript
  • #field provides real privacy at the execution level of JavaScript
  • Using # requires support for modern JS (ES2021+)

Trick questions.

Can you access a private field of a TypeScript class by direct access in JS?

Yes, you can, as privacy is only implemented at compile time. In the original JS, the field will be a regular object property. For example:

class A { private x = 1; } const a = new A(); console.log((a as any)["x"]); // 1

Can private fields with # be used in interfaces or described using type?

No, #private fields are part of the class implementation only and cannot be described in interfaces, types, or used outside the class itself. Interfaces only describe public members.

Can private fields declared with # be inherited?

No, such fields are not accessible to subclassing classes at all. Only the class itself has access to them:

class Parent { #foo = 123; } class Child extends Parent { // this.#foo = 444; // Error }

Common mistakes and anti-patterns

  • Using private to protect critical data — leads to vulnerabilities with direct access
  • Confusion between privacy at the type level and runtime
  • Attempting to use #fields in older versions of JS

Real-life example

Negative case

In a project for storing passwords in the User class, a private password field was used. One of the developers accidentally accesses it via user['password'] for debugging, and the field gets modified by an external module.

Pros:

  • Simple integration with TypeScript

Cons:

  • No real protection at JS runtime
  • Easy to accidentally bypass privacy

Positive case

A developer uses the ES private field #password. Now attempts to access the field through indexing or in subclasses do not go through, ensuring data security even when using third-party libraries and tools.

Pros:

  • Real privacy
  • Reliable protection of sensitive properties

Cons:

  • Requires modern JS support (not compatible with ES5)
  • Private fields are not visible in interfaces, cannot be overridden in subclasses