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
publicmeans anyone can use the member. It is the default, so you rarely write it.privatemeans only code inside the same class can use the member.protectedmeans 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.
- Declare the method. Below the constructor, add
getName(): string { }. - 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.nameis a compile error. Callrex.getName()instead. - Returning the wrong thing.
return name;withoutthis.does not return the field. The field is reached throughthis. - Forgetting the return. A
stringmethod with noreturnstatement is a compile error, and a missing value would show up asundefined. - Assuming
privateprotects at run time. It is a compiler check only. - Using
privatein a subclass. A subclass cannot read its parent's private field either. Useprotectedwhen subclasses need access.
Key takeaways
privatelimits a member to the class that declares it,protectedadds its subclasses, andpublicis the default.- Expose a private field through a method such as
getName(). - TypeScript's
privateis checked by the compiler only.#nameis enforced by JavaScript at run time.