TypeScript adds type annotations and access modifiers to JavaScript classes.

Basic Class

class Person {
  name: string;
  age: number;

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

  greet(): string {
    return `Hello, I'm ${this.name} and I'm ${this.age} years old.`;
  }
}

const person = new Person("Alice", 25);
console.log(person.greet());

Access Modifiers

public (default)

Properties and methods are accessible from anywhere.

class Person {
  public name: string;  // public is optional (default)
  public age: number;

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

private

Only accessible within the class.

class BankAccount {
  private balance: number = 0;

  deposit(amount: number): void {
    this.balance += amount;
  }

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

const account = new BankAccount();
account.deposit(100);
// account.balance; // ❌ Error: Property 'balance' is private
console.log(account.getBalance()); // ✅ OK

protected

Accessible within the class and subclasses.

class Animal {
  protected name: string;

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

class Dog extends Animal {
  bark(): void {
    console.log(`${this.name} barks!`); // ✅ OK, name is protected
  }
}

const dog = new Dog("Buddy");
// dog.name; // ❌ Error: Property 'name' is protected

readonly

Properties that can only be set during initialization.

class Person {
  readonly id: number;
  name: string;

  constructor(id: number, name: string) {
    this.id = id;  // ✅ OK, setting during initialization
    this.name = name;
  }
}

const person = new Person(1, "Alice");
// person.id = 2; // ❌ Error: Cannot assign to 'id' because it is a read-only property

Parameter Properties

Shorthand for declaring and initializing properties.

// Long form
class Person {
  name: string;
  age: number;

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

// Short form with parameter properties
class Person {
  constructor(public name: string, public age: number) {
    // Automatically creates and assigns this.name and this.age
  }
}

Getters and Setters

class Temperature {
  private _celsius: number = 0;

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

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

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

  set fahrenheit(value: number) {
    this.celsius = (value - 32) * 5/9;
  }
}

const temp = new Temperature();
temp.celsius = 25;
console.log(temp.fahrenheit); // 77

Inheritance

class Animal {
  name: string;

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

  move(distance: number = 0): void {
    console.log(`${this.name} moved ${distance}m.`);
  }
}

class Dog extends Animal {
  breed: string;

  constructor(name: string, breed: string) {
    super(name);  // Call parent constructor
    this.breed = breed;
  }

  bark(): void {
    console.log("Woof! Woof!");
  }
}

const dog = new Dog("Buddy", "Golden Retriever");
dog.move(10);  // Inherited method
dog.bark();    // Dog-specific method

Abstract Classes

Cannot be instantiated directly, must be extended.

abstract class Animal {
  name: string;

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

  abstract makeSound(): void;  // Must be implemented by subclasses

  move(): void {
    console.log(`${this.name} is moving.`);
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log("Woof!");
  }
}

// const animal = new Animal("Generic"); // ❌ Error: Cannot create instance of abstract class
const dog = new Dog("Buddy");
dog.makeSound();

Static Members

Belong to the class itself, not instances.

class MathHelper {
  static PI: number = 3.14159;

  static add(a: number, b: number): number {
    return a + b;
  }
}

console.log(MathHelper.PI);        // 3.14159
console.log(MathHelper.add(5, 3)); // 8

// const helper = new MathHelper();
// helper.PI; // ❌ Error: Property 'PI' does not exist on type 'MathHelper'

Implementing Interfaces

Classes can implement interfaces.

interface Flyable {
  fly(): void;
}

interface Swimmable {
  swim(): void;
}

class Duck implements Flyable, Swimmable {
  fly(): void {
    console.log("Flying...");
  }

  swim(): void {
    console.log("Swimming...");
  }
}

Exercises

Exercise 1: Basic Class

// Create a class called 'Car' with:
// - make (string)
// - model (string)
// - year (number)
// - Constructor that initializes all properties
// - Method 'getInfo()' that returns a string with car details

Exercise 2: Access Modifiers

// Create a class called 'BankAccount' with:
// - private balance (number, initialized to 0)
// - public deposit(amount: number): void
// - public withdraw(amount: number): void
// - public getBalance(): number

Exercise 3: Inheritance

// Create a base class 'Shape' with:
// - protected name (string)
// - abstract method area(): number
// Then create 'Circle' that extends 'Shape' with:
// - private radius (number)
// - Implements area() method

Exercise 4: Getters and Setters

// Create a class 'Rectangle' with:
// - private width and height
// - getter and setter for width (validate > 0)
// - getter and setter for height (validate > 0)
// - getter for area (width * height)

Exercise 5: Static Members

// Create a class 'StringUtils' with:
// - static method reverse(str: string): string
// - static method capitalize(str: string): string
// - static method isEmpty(str: string): boolean

Best Practices

  1. Use access modifiers - Encapsulate your data properly
  2. Prefer private over public - Only expose what's necessary
  3. Use readonly for immutable properties - When values shouldn't change
  4. Use abstract classes for base functionality - When you want shared implementation
  5. Use interfaces for contracts - When you only need structure

Common Mistakes

  1. Forgetting to call super() in derived classes
// ❌ Bad
class Dog extends Animal {
  constructor(name: string) {
    // Missing super() call
    this.name = name;
  }
}
  1. Using public when private would work
// ❌ Bad
class BankAccount {
  public balance: number;  // Should be private
}

// ✅ Good
class BankAccount {
  private balance: number;
}
  1. Not using parameter properties
// ❌ Verbose
class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// ✅ Concise
class Person {
  constructor(public name: string, public age: number) {}
}

Next Steps

Learn about Arrays and Tuples.