Skip to content

TypeScript Classes — Complete Guide

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about TypeScript Classes. We cover key concepts, practical examples, and best practices to help you master this topic.

TypeScript classes extend JavaScript ES6 classes with type annotations, access modifiers, parameter properties, and interface implementation — giving you a familiar OOP syntax with compile-time type safety.

What You'll Learn

  • Class syntax with typed properties and methods
  • Access modifiers: public, private, protected
  • Parameter properties shorthand
  • readonly properties
  • implements for interface conformance

Why It Matters

Classes are the foundation of object-oriented programming in TypeScript. While TypeScript also supports functional patterns, many frameworks (Angular, NestJS) and Design Patterns rely heavily on classes. Properly typed classes serve as self-documenting blueprints for your objects.

Real-World Use

NestJS (used at DodaTech for internal services) relies on classes with decorators for Dependency Injection, controllers, and services. The private and readonly modifiers ensure that service dependencies are encapsulated and never accidentally reassigned.

Learning Path

flowchart LR
  A[Type Manipulation] --> B[Classes]
  B --> C[Abstract Classes]
  B --> D[You Are Here]
  C --> E[Inheritance]
  E --> F[Decorators]

Class Basics

class User {
  name: string;
  age: number;
  email: string;

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

  greet(): string {
    return `Hello, my name is ${this.name}`;
  }

  isAdult(): boolean {
    return this.age >= 18;
  }
}

const alice = new User("Alice", 30, "alice@example.com");
console.log(alice.greet());    // Hello, my name is Alice
console.log(alice.isAdult());  // true

Think of a class as a blueprint and instances (objects created with new) as houses built from that blueprint.

Access Modifiers

public (default)

Accessible everywhere:

class Car {
  public make: string = "Toyota";  // public is the default
  public model: string = "Camry";

  public drive(): void {
    console.log("Driving...");
  }
}

const car = new Car();
console.log(car.make); // OK — public
car.drive();           // OK — public

private

Accessible only within the class:

class BankAccount {
  private balance: number = 0;

  public deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount;
    }
  }

  public getBalance(): number {
    return this.balance;
  }

  private logTransaction(type: string, amount: number): void {
    console.log(`${type}: ${amount}`);
  }
}

const account = new BankAccount();
account.deposit(100);
// account.balance; // Error: Property 'balance' is private
// account.logTransaction("deposit", 100); // Error: private method

protected

Accessible within the class and subclasses:

class Animal {
  protected sound: string = "";

  protected makeSound(): void {
    console.log(this.sound);
  }
}

class Dog extends Animal {
  constructor() {
    super();
    this.sound = "Woof!"; // OK — protected property accessible in subclass
  }

  public bark(): void {
    this.makeSound(); // OK — protected method
  }
}

const dog = new Dog();
dog.bark();           // OK — public
// dog.sound;         // Error: protected
// dog.makeSound();   // Error: protected

Parameter Properties

TypeScript's shorthand for declaring and initializing class properties in the constructor:

// Verbose way
class UserVerbose {
  private name: string;
  public age: number;

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

// Shorthand — same thing
class UserShorthand {
  constructor(
    private name: string,
    public age: number,
    readonly email: string = "no-email",
  ) {}
}

const user = new UserShorthand("Alice", 30, "alice@example.com");
console.log(user.age);   // 30
// console.log(user.name); // Error: private

Parameter properties save 3-4 lines per constructor parameter and are idiomatic TypeScript.

Readonly Properties

Prevent reassignment after construction:

class Config {
  readonly apiKey: string;
  readonly endpoint: string;
  timeout: number; // Can change

  constructor(apiKey: string, endpoint: string, timeout: number = 5000) {
    this.apiKey = apiKey;
    this.endpoint = endpoint;
    this.timeout = timeout;
  }
}

const config = new Config("sk-abc", "https://api.example.com");
// config.apiKey = "new-key"; // Error: Cannot assign to 'readonly' property
config.timeout = 10000; // OK — not readonly

implements

A class can implement an interface, ensuring it conforms to a contract:

interface Printable {
  print(): void;
}

interface Serializable {
  toJSON(): string;
}

class Report implements Printable, Serializable {
  constructor(private title: string, private content: string) {}

  print(): void {
    console.log(`Title: ${this.title}`);
    console.log(`Content: ${this.content}`);
  }

  toJSON(): string {
    return JSON.stringify({ title: this.title, content: this.content });
  }
}

const report = new Report("Scan Results", "No threats found");
report.print();
// Title: Scan Results
// Content: No threats found

Getter and Setter

class Temperature {
  private _celsius: number = 0;

  get celsius(): number {
    return this._celsius;
  }

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

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

const temp = new Temperature();
temp.celsius = 25;
console.log(`${temp.celsius}°C = ${temp.fahrenheit}°F`);
// 25°C = 77°F

Common Mistakes

1. Forgetting to Initialize Properties

class User {
  name: string; // Error with strictPropertyInitialization: not initialized
  // Fix: name: string = "";
  // Or: constructor(public name: string) {}
}

Enable strictPropertyInitialization: true (included in strict) to catch this.

2. Using private Instead of JavaScript # Private Fields

TypeScript's private is a compile-time check. JavaScript's # is truly private at runtime:

class TSPrivate {
  private secret = "hidden";
}

class JSPrivate {
  #secret = "hidden";
}

const ts = new TSPrivate();
(ts as any).secret; // Accessible at runtime via type assertion

const js = new JSPrivate();
// (js as any).#secret; // Syntax error — truly private

Prefer # for true runtime privacy; use TypeScript's private for compile-time only.

3. Not Using Parameter Properties

// 7 extra lines of boilerplate
class Verbose {
  private x: number;
  private y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

// 1 line
class Concise {
  constructor(private x: number, private y: number) {}
}

4. Putting Type Annotations Where TypeScript Can Infer

class User {
  name: string; // OK: class properties need explicit types
  age: number;

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

  greet(): string { // Explicit return type is good practice
    return `Hi, I'm ${this.name}`;
  }
}

5. Confusing implements with extends

implements checks that a class satisfies an interface. extends inherits implementation from another class.

Practice Questions

  1. What is the default access modifier in TypeScript classes? public — all members are public unless explicitly marked private or protected.

  2. What do parameter properties do? They combine parameter declaration and property initialization in the constructor: constructor(private name: string) {}

  3. What is the difference between private and protected? private members are accessible only within the declaring class. protected members are also accessible in subclasses.

  4. Can a class implement multiple interfaces? Yes: class MyClass implements InterfaceA, InterfaceB {}

Challenge: Write a Vector2D class with private x and y fields, a constructor, getters, a length() method, and an add(other: Vector2D): Vector2D method. Use parameter properties.

FAQ

Can I use access modifiers in JavaScript?

No. TypeScript's access modifiers (public, private, protected) are compile-time only and are erased in the output JavaScript.

What is the `readonly` modifier?

It prevents reassignment of a property after construction. It does not prevent mutation of arrays/objects.

Can I use both `private` and `readonly` together?

Yes: private readonly id: string; — accessible only within the class and cannot be reassigned.

What is `strictPropertyInitialization`?

A TypeScript compiler option that ensures all class properties are initialized in the constructor or have default values.

Do getters and setters affect performance?

Slightly — they are function calls. In most applications the impact is negligible. Use them for validation and computed properties.

Mini Project: Task Manager Class

// src/task-manager.ts

interface Task {
  id: string;
  title: string;
  completed: boolean;
  createdAt: Date;
}

class TaskManager {
  private tasks: Task[] = [];

  constructor(private owner: string) {}

  addTask(title: string): Task {
    const task: Task = {
      id: `task-${Date.now()}`,
      title,
      completed: false,
      createdAt: new Date(),
    };
    this.tasks.push(task);
    return task;
  }

  completeTask(id: string): boolean {
    const task = this.tasks.find(t => t.id === id);
    if (!task) return false;
    task.completed = true;
    return true;
  }

  getPendingTasks(): Task[] {
    return this.tasks.filter(t => !t.completed);
  }

  getCompletedTasks(): Task[] {
    return this.tasks.filter(t => t.completed);
  }

  get summary(): string {
    return `${this.owner}: ${this.tasks.length} tasks (${this.getPendingTasks().length} pending)`;
  }
}

const manager = new TaskManager("Alice");
manager.addTask("Learn TypeScript classes");
manager.addTask("Build a project");
manager.addTask("Write tests");
manager.completeTask(manager.getPendingTasks()[0].id);

console.log(manager.summary);
// Alice: 3 tasks (2 pending)
console.log(`Completed: ${manager.getCompletedTasks().length}`);
// Completed: 1

What's Next

Now explore abstract classes for defining base templates:

Lesson Description
{{< ref "/programming-languages/typescript/18-type-manipulation" >}} Review type manipulation
{{< ref "/programming-languages/typescript/20-abstract-classes" >}} Abstract classes and static members
{{< ref "/programming-languages/typescript/21-inheritance" >}} Inheritance and method overriding

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro