Skip to content

TypeScript - Classes

Classes are a fundamental concept in object-oriented programming (OOP) and serve as blueprints for creating objects. They encapsulate data (properties or fields) and behavior (methods or functions) related to a specific entity. TypeScript provides first-class support for the class syntax introduced in ES6, enhanced with features like type annotations and access modifiers.

Use the class keyword followed by the class name (typically PascalCase).

class ClassName {
// Fields (Properties)
propertyName: type;
anotherProperty?: type; // Optional property
readonly readOnlyProp: type = initialValue; // Readonly property
private privateField: type;
protected protectedField: type;
static staticProperty: type = value;
// Constructor
constructor(param1: type, param2: type, ...) {
// Initialization logic
this.propertyName = param1;
// ...
}
// Methods
methodName(param: type): returnType {
// Method logic
return value;
}
private privateMethod(): void { ... }
protected protectedMethod(): void { ... }
static staticMethod(): void { ... }
// Getters & Setters
get computedValue(): type {
return this.propertyName;
}
set computedValue(value: type) {
this.propertyName = value;
}
}

Class definitions can include:

  • Fields (Properties): Variables declared within the class scope that hold data for instances of the class. Use type annotations.
  • Constructor: A special method named constructor responsible for creating and initializing objects created with the class. Called when you use the new keyword.
  • Methods: Functions defined within the class that define the behavior of objects.
  • Access Modifiers: Keywords (public, private, protected) that control the visibility and accessibility of class members.
  • Readonly Modifier: Prevents modification of a property after its initial assignment (usually in the constructor).
  • Static Members: Properties or methods belonging to the class itself, not instances. Accessed via ClassName.memberName.
  • Getters/Setters: Special methods that provide controlled access to object properties.
class Vehicle {
// Properties with default access (public)
make: string;
model: string;
year: number;
private _vin: string; // Private property convention
readonly id: string;
// Static property
static numberOfVehicles = 0;
constructor(make: string, model: string, year: number, vin: string) {
this.make = make;
this.model = model;
this.year = year;
this._vin = vin;
this.id = `veh-${Math.random().toString(36).substr(2, 9)}`; // Assign readonly property
Vehicle.numberOfVehicles++; // Increment static property
}
// Method
displayInfo(): void {
console.log(`Vehicle: ${this.year} ${this.make} ${this.model} (ID: ${this.id})`);
}
// Getter for private property
get vin(): string {
return this._vin;
}
// Static method
static getVehicleCount(): number {
return Vehicle.numberOfVehicles;
}
}
// Accessing static member
console.log(`Initial vehicle count: ${Vehicle.getVehicleCount()}`); // Output: 0

Use the new keyword followed by the class name and parentheses (passing arguments to the constructor if required).

const car1 = new Vehicle("Toyota", "Camry", 2022, "123ABC456DEF");
const car2 = new Vehicle("Honda", "Civic", 2021, "789GHI012JKL");
console.log(`Current vehicle count: ${Vehicle.getVehicleCount()}`); // Output: 2

Use the dot (.) notation on the object instance to access its public properties and methods.

console.log(car1.make); // Output: Toyota
car1.displayInfo(); // Output: Vehicle: 2022 Toyota Camry (ID: veh-...)
console.log(car2.model); // Output: Civic
car2.displayInfo(); // Output: Vehicle: 2021 Honda Civic (ID: veh-...)
// Accessing via getter
console.log(`Car 1 VIN: ${car1.vin}`);
// car1.id = "new-id"; // Error: Cannot assign to 'id' because it is a read-only property.
// console.log(car1._vin); // Error: Property '_vin' is private and only accessible within class 'Vehicle'.
  • public (Default): Members are accessible from anywhere.
  • private: Members are only accessible from within the defining class itself.
  • protected: Members are accessible within the defining class and by instances of derived (sub)classes.

TypeScript offers a concise syntax to declare and initialize class members directly from constructor parameters by adding an access modifier (public, private, protected) or readonly.

class Employee {
// Automatically creates and initializes 'name' and 'department' properties
constructor(
public readonly id: number,
public name: string,
private department: string
) {}
getDept(): string {
// Can access private member within the class
return this.department;
}
}
const emp1 = new Employee(101, "Alice", "Engineering");
console.log(emp1.name); // Output: Alice
console.log(emp1.id); // Output: 101
console.log(emp1.getDept()); // Output: Engineering
// console.log(emp1.department); // Error: Property 'department' is private...

Classes can inherit properties and methods from a single parent class (base class or superclass) using the extends keyword. The inheriting class is called a subclass or derived class.

Derived classes inherit all public and protected members. Private members are not inherited. Constructors are not inherited, but the derived class constructor must call the parent constructor using super() if the parent has a constructor.

class Animal {
constructor(public name: string) {}
move(distanceInMeters: number = 0): void {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
// Derived constructor must call super()
constructor(name: string) {
super(name); // Calls the parent class (Animal) constructor
}
bark(): void {
console.log("Woof! Woof!");
}
// Overriding the parent method
move(distanceInMeters = 5): void {
console.log("Running...");
super.move(distanceInMeters); // Call the parent class's move method
}
}
const myDog = new Dog("Buddy");
myDog.bark(); // Output: Woof! Woof!
myDog.move(10); // Output: Running...
// Output: Buddy moved 10m.
console.log(myDog.name); // Output: Buddy (inherited from Animal)

Derived classes can provide their own implementation for a method inherited from the parent class (overriding). Use the super keyword to call methods from the parent class (super.methodName()) or the parent constructor (super() inside the derived constructor).

Classes marked with abstract cannot be instantiated directly. They often serve as base classes and may contain abstract methods (methods without implementation) that must be implemented by derived classes.

abstract class Department {
constructor(public name: string) {}
printName(): void {
console.log(`Department name: ${this.name}`);
}
abstract printMeeting(): void; // Must be implemented by derived classes
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
}
// const dept = new Department("General"); // Error: Cannot create an instance of an abstract class.
const accounting = new AccountingDepartment();
accounting.printName();
accounting.printMeeting();

Classes Implementing Interfaces (implements)

Section titled “Classes Implementing Interfaces (implements)”

Classes can implement one or more interfaces using the implements keyword. This enforces that the class adheres to the structure defined by the interface(s).

interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class DigitalClock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date): void {
this.currentTime = d;
}
}
const clock = new DigitalClock();
console.log(clock.currentTime);