Start learning free

LESSON 185 OF 348 · OBJECT-ORIENTED PROGRAMMING

Access Modifiers in TypeScript: Private Fields

Lesson Read the full explanation

TL ; DR:

  • Modern JavaScript (ES2022+) has true private fields with the # prefix:

    class Dog {
        #name;
    
        constructor(name) {
            this.#name = name;
        }
    
        getName() {
            return this.#name;
        }
    }
    

  • Outside the class, rex.#name is a syntax error — the runtime enforces it.

  • Older code uses the underscore convention (this._name), which is just a hint and not enforced.



Full lesson:

For decades, JavaScript classes had no real privacy — programmers used a leading underscore (_name) as a convention. ES2022 added true private fields with the # prefix.


How # private fields work

  • You must declare the field at the top of the class body: #name;
  • You access it as this.#name — never as a string or via this[...].
  • Trying to access from outside is a syntax error, not a runtime error.

Your task

The Dog class declares #name and the constructor stores into it. Add a getName() method that returns this.#name.

Dog's name: Rex
STEP 1 OF 2
    Ctrl + Enter to check

    TYPESCRIPT · OBJECT-ORIENTED PROGRAMMING

    Access Modifiers in TypeScript, explained

    Why hide a field

    When every field is open, any code anywhere can read it or overwrite it with a nonsense value. A class that guards its data is easier to trust: the rest of the program has to go through the methods the class offers, and the class decides what is allowed. This is called encapsulation. TypeScript supports it with access modifiers, keywords placed before a member that say who may use it.

    The modifiers

    • public means anyone can use the member. It is the default, so you rarely write it.
    • private means only code inside the same class can use the member.
    • protected means the class and its subclasses can use it, but code outside cannot.

    The lesson uses private:

    class Dog {
        private name: string;
    
        constructor(name: string) {
            this.name = name;
        }
    }
    

    Inside the class, including the constructor, this.name works as usual. Outside, the compiler refuses:

    const rex = new Dog("Rex");
    console.log(rex.name);
    
    error TS2341: Property 'name' is private and only accessible within class 'Dog'.
    

    Reading a private field through a method

    If outside code needs the value, the class provides a method that returns it. This kind of method is usually called a getter, and its name starts with get by convention:

    getName(): string {
        return this.name;
    }
    

    The return type is string because the method gives back the name. A method of the class can read the private field, so the code compiles, and callers get the value without being able to change it.

    A getter also leaves room to change the class later. The field could be renamed, stored in a different form or computed on demand, and every caller of getName() would keep working, because nobody outside depended on the field itself. A matching method that changes the field can check the new value first and refuse a bad one, which direct access to a public field never allows.

    The exercise

    The starter code gives you the class with its private name field and constructor, plus code that creates rex and prints rex.getName(). Only the method is missing.

    1. Declare the method. Below the constructor, add getName(): string { }.
    2. Return the field. Inside it, write return this.name;.
    class Dog {
        private name: string;
    
        constructor(name: string) {
            this.name = name;
        }
    
        getName(): string {
            return this.name;
        }
    }
    
    const rex = new Dog("Rex");
    console.log("Dog's name: " + rex.getName());
    

    The output is:

    Dog's name: Rex
    

    What private does and does not do

    TypeScript's private is a compile-time check. Like all type information, it is erased when the code is compiled to JavaScript, so at run time the field is an ordinary property. If you ignore the compiler's error, the JavaScript still runs and rex.name gives Rex. The same goes for code that gets around the check with a cast such as (rex as any).name. And JSON.stringify(rex) includes the private field: {"name":"Rex"}.

    JavaScript has its own, stronger form of privacy: a field whose name starts with #, written #name and used as this.#name. TypeScript supports it too. The runtime enforces it, so the field is not a normal property, and it does not show up in Object.keys or in JSON.stringify:

    class Cat {
        #name: string;
        constructor(name: string) { this.#name = name; }
        getName(): string { return this.#name; }
    }
    const tom = new Cat("Tom");
    console.log(tom.getName(), Object.keys(tom), JSON.stringify(tom));
    
    Tom [] {}
    

    Use private to document and check who should touch a field while you write TypeScript. Use # when the privacy must hold at run time as well.

    Common mistakes

    • Reading the field from outside. rex.name is a compile error. Call rex.getName() instead.
    • Returning the wrong thing. return name; without this. does not return the field. The field is reached through this.
    • Forgetting the return. A string method with no return statement is a compile error, and a missing value would show up as undefined.
    • Assuming private protects at run time. It is a compiler check only.
    • Using private in a subclass. A subclass cannot read its parent's private field either. Use protected when subclasses need access.

    Key takeaways

    • private limits a member to the class that declares it, protected adds its subclasses, and public is the default.
    • Expose a private field through a method such as getName().
    • TypeScript's private is checked by the compiler only. #name is enforced by JavaScript at run time.
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free