Classes
TypeScript Programming
Classes in TypeScript
TypeScript classes build on the ES6 class syntax by adding type annotations, access modifiers, abstract classes, and parameter properties. While JavaScript classes provide the runtime behavior, TypeScript's additions enable you to enforce encapsulation at compile time — restricting which parts of a class are accessible from outside and ensuring that subclasses implement required methods. These features make TypeScript classes suitable for building large, maintainable object-oriented systems where clear contracts between components are essential.
Typed Properties and Constructors
Class properties must be declared with their types before they can be used. TypeScript requires that all properties are either initialized in the declaration or assigned in the constructor when strictPropertyInitialization is enabled.
class User {
id: number;
name: string;
email: string;
createdAt: Date;
constructor(id: number, name: string, email: string) {
this.id = id;
this.name = name;
this.email = email;
this.createdAt = new Date();
}
getDisplayName(): string {
return `${this.name} (${this.email})`;
}
}
const user = new User(1, "Alice", "alice@example.com");
console.log(user.getDisplayName());
Access Modifiers
TypeScript provides three access modifiers: public (default, accessible everywhere), private (accessible only within the class), and protected (accessible within the class and its subclasses). These modifiers exist only at compile time and are not enforced at runtime.
class BankAccount {
public owner: string;
private balance: number;
protected accountNumber: string;
constructor(owner: string, initialBalance: number) {
this.owner = owner;
this.balance = initialBalance;
this.accountNumber = this.generateAccountNumber();
}
// Public method — accessible from anywhere
public deposit(amount: number): void {
if (amount <= 0) throw new Error("Amount must be positive");
this.balance += amount;
}
public getBalance(): number {
return this.balance;
}
// Private method — only accessible within this class
private generateAccountNumber(): string {
return `ACC-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}
}
const account = new BankAccount("Alice", 1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// account.balance; // Error: Property 'balance' is private
// account.accountNumber; // Error: Property 'accountNumber' is protected
Parameter Properties
TypeScript offers a shorthand for declaring and initializing properties directly in the constructor parameter list by prefixing them with an access modifier.
class Product {
constructor(
public readonly id: number,
public name: string,
public price: number,
private stock: number = 0
) {}
isInStock(): boolean {
return this.stock > 0;
}
sell(quantity: number): void {
if (quantity > this.stock) {
throw new Error("Insufficient stock");
}
this.stock -= quantity;
}
}
const item = new Product(1, "Widget", 9.99, 100);
console.log(item.name); // "Widget"
console.log(item.isInStock()); // true
Abstract Classes
Abstract classes cannot be instantiated directly. They serve as base classes that define common structure and behavior while requiring subclasses to implement specific abstract methods. This enforces that all subclasses provide certain functionality.
abstract class Shape {
constructor(public color: string) {}
// Abstract methods must be implemented by subclasses
abstract area(): number;
abstract perimeter(): number;
// Concrete method shared by all subclasses
describe(): string {
return `${this.color} shape: area=${this.area().toFixed(2)}, perimeter=${this.perimeter().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(color: string, public radius: number) {
super(color);
}
area(): number {
return Math.PI * this.radius ** 2;
}
perimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(color: string, public width: number, public height: number) {
super(color);
}
area(): number {
return this.width * this.height;
}
perimeter(): number {
return 2 * (this.width + this.height);
}
}
// const s = new Shape("red"); // Error: Cannot create an instance of an abstract class
const circle = new Circle("red", 5);
const rect = new Rectangle("blue", 4, 6);
console.log(circle.describe()); // "red shape: area=78.54, perimeter=31.42"
console.log(rect.describe()); // "blue shape: area=24.00, perimeter=20.00"
Tip: Use abstract classes when you want to share implementation code between related classes. Use interfaces when you only need to define a contract without shared behavior.
Key Takeaways
- TypeScript class properties must be declared with types and initialized before use.
- Access modifiers (
public,private,protected) enforce encapsulation at compile time. - Parameter properties in constructors reduce boilerplate by combining declaration and initialization.
- Abstract classes define contracts that subclasses must fulfill while sharing common behavior.
- Use
readonlyon properties that should not change after construction.