Classes

TypeScript classes are JavaScript classes with types added: typed properties, typed constructor parameters and methods, and access modifiers that control which members can be reached from outside the class.

A typed class

TS account.ts
class BankAccount {
  private balance: number;
  public readonly owner: string;

  constructor(owner: string, startingBalance: number) {
    this.owner = owner;
    this.balance = startingBalance;
  }

  deposit(amount: number): void {
    this.balance += amount;
    console.log(`Deposited ${amount}. New balance: ${this.balance}`);
  }
}

const acc = new BankAccount("Priya", 100);
acc.deposit(50);
console.log(acc.balance);
tsc output
error TS2341: Property 'balance' is private and only accessible within class 'BankAccount'.

Each property is declared in the class body with its type. With strict mode on, every property must be initialised — either where it's declared or in the constructor — or TypeScript reports that it might be undefined.

Access modifiers

ModifierAccessible from
public (default)Anywhere
protectedThe class and its subclasses (lesson 18)
privateOnly inside the class itself

The last line of the example above fails because it reaches into a private property from outside. Remove it and the program runs normally.

readonly properties

TS readonly.ts
class Invoice {
  readonly number: string;
  constructor(n: string) {
    this.number = n;
  }
}

const inv = new Invoice("INV-001");
inv.number = "INV-002";
tsc output
error TS2540: Cannot assign to 'number' because it is a read-only property.

A readonly property can be assigned in its declaration or in the constructor, and never again.

Parameter properties

Declaring a property, accepting it as a constructor parameter and assigning it is so common that TypeScript has a shorthand. Put a modifier in front of a constructor parameter and it becomes a property automatically:

TS parameter-properties.ts
class Point {
  constructor(public readonly x: number, public readonly y: number) {}

  distanceTo(other: Point): number {
    return Math.hypot(other.x - this.x, other.y - this.y);
  }
}

const a = new Point(0, 0);
const b = new Point(3, 4);
console.log(a.distanceTo(b));
Output
5

That one-line constructor declares x and y, and assigns them. Note that parameter properties are one of the few TypeScript features that generate code, so tools that only strip types (like Node's built-in type stripping) don't support them.

Implementing an interface

TS shape.ts
interface Shape {
  area(): number;
  describe(): string;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}

  area(): number {
    return this.width * this.height;
  }

  describe() {
    return `${this.width}×${this.height} rectangle`;
  }
}

const rect = new Rectangle(4, 5);
console.log(rect.describe(), "=", rect.area());
Output
4×5 rectangle = 20

implements Shape makes the compiler check that Rectangle provides everything Shape requires, with compatible types. A class can implement several interfaces: class A implements B, C.

A class is also a type

Declaring a class creates two things: a constructor function (a runtime value) and a type describing its instances. You can use the class name in annotations:

TS class-as-type.ts
class Tag {
  constructor(public name: string) {}
}

function render(tags: Tag[]): string {
  return tags.map(t => `#${t.name}`).join(" ");
}

console.log(render([new Tag("ts"), new Tag("web")]));
Output
#ts #web

TypeScript private vs JavaScript #private

private is checked only by the compiler. After compilation, the property is an ordinary property that any JavaScript code can read. JavaScript's own private fields, written with #, are enforced at runtime:

TS hash-private.ts
class Wallet {
  #coins = 0;

  add(n: number) {
    this.#coins += n;
    return this.#coins;
  }
}

const w = new Wallet();
console.log(w.add(3));
console.log(Object.keys(w));
Output
3
[]

#coins doesn't even appear in Object.keys. Use # when real runtime privacy matters (for example, in a library), and private when compile-time checking is enough.

Try it yourself

Try it yourself
Console output