TypeScript Classes

TypeScript adds type annotations, access modifiers, and parameter properties to JavaScript classes. Classes define blueprints for objects — they bundle data (properties) and behaviour (methods) together.

Basic Class

TypeScript
class Student {
  name: string;
  grade: number;

  constructor(name: string, grade: number) {
    this.name = name;
    this.grade = grade;
  }

  isPassing(): boolean {
    return this.grade >= 40;
  }

  toString(): string {
    return `${this.name} (${this.grade})`;
  }
}

const ram = new Student("Ram", 85);
console.log(ram.isPassing()); // true
console.log(ram.toString());  // "Ram (85)"

Parameter Properties (Shorthand)

TypeScript can declare and assign properties in one step by adding an access modifier to the constructor parameter:

TypeScript
// Longhand
class Student {
  name: string;
  grade: number;
  constructor(name: string, grade: number) {
    this.name = name;
    this.grade = grade;
  }
}

// Shorthand — identical result
class Student {
  constructor(
    public name: string,
    public grade: number,
    private _id: string,
  ) {}
}
Modifier Class Subclass Outside
public ✅ ✅ ✅
protected ✅ ✅ ❌
private ✅ ❌ ❌
readonly ✅ (cannot modify) ✅ ✅

Methods

TypeScript
class Calculator {
  private result: number = 0;

  add(n: number): this {
    this.result += n;
    return this; // enables chaining
  }

  multiply(n: number): this {
    this.result *= n;
    return this;
  }

  getValue(): number {
    return this.result;
  }
}

const calc = new Calculator();
const value = calc.add(5).multiply(3).getValue(); // 15

Inheritance

TypeScript
class Animal {
  constructor(public name: string) {}

  speak(): string {
    return `${this.name} makes a sound`;
  }
}

class Dog extends Animal {
  constructor(name: string, public breed: string) {
    super(name); // call parent constructor
  }

  override speak(): string {
    return `${this.name} barks`;
  }
}

const rex = new Dog("Rex", "Labrador");
console.log(rex.speak()); // "Rex barks"

Use the override keyword (TypeScript 4.3+) when overriding a parent method — it prevents typos and ensures the parent actually has that method.

Abstract Classes

Abstract classes cannot be instantiated directly — they are base classes that other classes extend:

TypeScript
abstract class Shape {
  abstract area(): number;  // subclasses MUST implement this

  describe(): string {
    return `Area: ${this.area().toFixed(2)}`;
  }
}

class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }

  area(): number {
    return Math.PI * this.radius ** 2;
  }
}

class Rectangle extends Shape {
  constructor(public width: number, public height: number) {
    super();
  }

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

// const s = new Shape();  // Error: Cannot instantiate abstract class
const c = new Circle(5);
console.log(c.describe()); // "Area: 78.54"

Getters and Setters

TypeScript
class Temperature {
  private _celsius: number;

  constructor(celsius: number) {
    this._celsius = celsius;
  }

  get fahrenheit(): number {
    return this._celsius * 9 / 5 + 32;
  }

  set celsius(value: number) {
    if (value < -273.15) throw new Error("Below absolute zero");
    this._celsius = value;
  }
}

const t = new Temperature(100);
console.log(t.fahrenheit); // 212
t.celsius = 0;
console.log(t.fahrenheit); // 32

Tips

  • Use parameter properties — they eliminate boilerplate.
  • Favour composition over inheritance — has-a is often better than is-a.
  • Use abstract classes when you need shared implementation across subclasses.
  • Keep classes small — one class, one responsibility.

Next: generics — write code that works with any type.