TypeScript course menu
TypeScript Classes: Typed Properties, public, private, protected, readonly and Parameter Properties
Learn classes in TypeScript: typed properties and constructors, access modifiers public, private and protected, readonly, parameter properties, TypeScript private versus JavaScript #private fields, getters and setters, static members, and classes as types.
On this page
TypeScript adds types and access control to JavaScript classes. Everything you know about JavaScript classes still applies; this lesson covers what TypeScript adds.
Typed properties#
Class properties are declared with their types in the class body:
class Product {
name: string;
price: number;
tags: string[] = [];
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
withTax(rate = 0.18): number {
return Math.round(this.price * (1 + rate));
}
}
const pen = new Product("Pen", 20);
pen.tags.push("stationery");
console.log(pen.name, pen.withTax(), pen.tags);Pen 24 [ 'stationery' ]In strict mode, every property must be initialised, either where it is declared or in the constructor:
class User {
name: string;
email: string;
constructor(name: string) {
this.name = name;
}
}error TS2564: Property 'email' has no initializer and is not definitely assigned in the constructor.Access modifiers#
class BankAccount {
private balance = 0;
deposit(amount: number): void {
this.balance += amount;
}
getBalance(): number {
return this.balance;
}
}
const account = new BankAccount();
account.deposit(500);
account.balance = 1000000;error TS2341: Property 'balance' is private and only accessible within class 'BankAccount'.protected members can be used in subclasses:
class Employee {
protected baseSalary: number;
constructor(public name: string, baseSalary: number) {
this.baseSalary = baseSalary;
}
salary(): number {
return this.baseSalary;
}
}
class Manager extends Employee {
salary(): number {
return this.baseSalary * 1.2; // allowed: protected
}
}
console.log(new Manager("Asha", 100000).salary());120000readonly#
readonly properties can be set in the declaration or the constructor, and never again:
class Order {
readonly id: number;
constructor(id: number) {
this.id = id;
}
}
const order = new Order(1042);
order.id = 1;error TS2540: Cannot assign to 'id' because it is a read-only property.Parameter properties#
Writing a property declaration, a constructor parameter and an assignment for each field is repetitive. Adding a modifier (public, private, protected or readonly) to a constructor parameter does all three at once:
class Student {
constructor(
public readonly id: number,
public name: string,
private marks: number[] = [],
) {}
addMark(mark: number): void {
this.marks.push(mark);
}
get average(): number {
return this.marks.length ? this.marks.reduce((a, b) => a + b, 0) / this.marks.length : 0;
}
}
const s = new Student(1, "Ravi");
s.addMark(80);
s.addMark(90);
console.log(s.id, s.name, s.average);1 Ravi 85Parameter properties are a TypeScript-only feature that generates code, so, like enums, they are not supported by tools that only strip types.
private versus #private#
TypeScript's private is only checked at compile time; at runtime the property is an ordinary property that JavaScript code (or a type assertion) could still read. JavaScript's own #private fields are enforced at runtime:
class A {
private secret = "compile-time only";
}
class B {
#secret = "runtime private";
reveal() {
return this.#secret;
}
}
console.log(Object.keys(new A()));
console.log(Object.keys(new B()), new B().reveal());[ 'secret' ]
[] runtime privateBoth are fine to use; #private is the stronger guarantee.
Getters, setters and static members#
These work as in JavaScript, with types:
class Temperature {
static readonly ABSOLUTE_ZERO = -273.15;
#celsius = 0;
get celsius(): number {
return this.#celsius;
}
set celsius(value: number) {
if (value < Temperature.ABSOLUTE_ZERO) throw new RangeError("Too cold");
this.#celsius = value;
}
get fahrenheit(): number {
return (this.#celsius * 9) / 5 + 32;
}
}
const t = new Temperature();
t.celsius = 25;
console.log(t.fahrenheit, Temperature.ABSOLUTE_ZERO);77 -273.15A class is also a type#
Declaring a class creates both a runtime constructor and a type with the same name, describing its instances:
class Point {
constructor(public x: number, public y: number) {}
}
function distance(a: Point, b: Point): number {
return Math.hypot(a.x - b.x, a.y - b.y);
}
console.log(distance(new Point(0, 0), new Point(3, 4)));
console.log(distance({ x: 0, y: 0 }, { x: 6, y: 8 })); // plain objects with the same shape work too5
10Because TypeScript is structural, any object with matching public members is accepted, even if it was not created with new Point.
Practice#
Practice exercise
Create a Library class with a private books array of { title: string; available: boolean }, a method add(title: string), a method borrow(title: string): boolean that marks a book as unavailable (returning false if it is missing or already borrowed), and a getter availableCount. Test it.
Show solution
type Book = { title: string; available: boolean };
class Library {
private books: Book[] = [];
add(title: string): void {
this.books.push({ title, available: true });
}
borrow(title: string): boolean {
const book = this.books.find((b) => b.title === title);
if (!book || !book.available) return false;
book.available = false;
return true;
}
get availableCount(): number {
return this.books.filter((b) => b.available).length;
}
}
const library = new Library();
library.add("Learning TS");
library.add("Web Basics");
console.log(library.borrow("Learning TS"), library.borrow("Learning TS"), library.borrow("Missing"));
console.log(library.availableCount);true false false
1Key takeaway#
Finished this lesson?