As a developer deeply immersed in TypeScript, I’ve found object-oriented programming (OOP) to be fundamental for creating robust, maintainable, and scalable applications. TypeScript doesn’t just support OOP—it enhances it significantly with type safety, intuitive access modifiers, and sophisticated typing capabilities that plain JavaScript simply can’t match. In this chapter, I’ll show you how to effectively structure your applications using classes, inheritance, access modifiers, and powerful advanced types.
1. TypeScript Classes: Defining Clear Structures
TypeScript classes bring the familiar JavaScript class syntax to a whole new level of type-safe clarity.
class Animal { name: string; constructor(name: string) { this.name = name; } move(distance: number): void { console.log(`${this.name} moved ${distance} meters.`); } } const dog = new Animal("Rex"); dog.move(10);

2. Access Modifiers in TypeScript: Public, Private, Protected, and Readonly
Enhance encapsulation and data protection by clearly defining the visibility of your class members.
- public: Accessible anywhere (default)
- private: Only accessible inside the class
- protected: Accessible inside the class and subclasses
- readonly: Cannot be reassigned after initialization
class BankAccount { public owner: string; private balance: number; protected accountType: string; readonly id: number; constructor(owner: string, id: number, type: string) { this.owner = owner; this.id = id; this.accountType = type; this.balance = 0; } deposit(amount: number) { this.balance += amount; } getBalance() { return this.balance; } }
3. Leveraging Inheritance and Abstract Classes in TypeScript
Inheritance allows you to build on existing structures, streamlining code reuse and organization.
class Bird extends Animal { fly() { console.log(`${this.name} is flying.`); } } const sparrow = new Bird("Sparrow"); sparrow.move(5); sparrow.fly();
Abstract classes provide a blueprint for other classes.
abstract class Shape { abstract area(): number; } class Circle extends Shape { constructor(private radius: number) { super(); } area(): number { return Math.PI * this.radius * this.radius; } }
4. Implementing Interfaces with TypeScript Classes
Ensure your classes fulfill contracts by implementing interfaces explicitly.
interface Printable { print(): void; } class Invoice implements Printable { print() { console.log("Printing invoice..."); } }
5. Advanced TypeScript Patterns: Unions, Intersections, and Discriminated Unions
Union Types: Allow a variable to represent one of several possible types.
type ID = string | number; let userId: ID = "abc123"; userId = 42;
Intersection Types: Combine multiple distinct types into one cohesive structure.
type Auditable = { createdAt: Date; updatedAt: Date }; type Product = { name: string; price: number }; type AuditedProduct = Product & Auditable;
Discriminated Unions: Model complex, conditional data structures with clearly defined discriminant fields.
type Square = { kind: "square"; size: number }; type Rectangle = { kind: "rectangle"; width: number; height: number }; type Shape = Square | Rectangle; function area(shape: Shape): number { switch (shape.kind) { case "square": return shape.size * shape.size; case "rectangle": return shape.width * shape.height; } }
6. TypeScript Mapped Types and Conditional Types
Mapped types dynamically create types based on existing ones, providing unmatched flexibility.
type Readonly<T> = { readonly [P in keyof T]: T[P]; }; type User = { id: number; name: string }; type ReadonlyUser = Readonly<User>; // All properties are readonly
Conditional types are types that depend on a condition.
type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
7. Essential TypeScript Utility Types
TypeScript includes several powerful utility types for concise and clear code:
- Partial – Makes all properties optional.
- Required – Ensures all properties are required.
- Pick<T, K> – Selects specific properties from a type.
- Omit<T, K> – Excludes specific properties from a type.
- Record<K, T> – Creates an object type with keys of type K and values of type T.
Example:
type Todo = { title: string; completed: boolean }; type PartialTodo = Partial<Todo>; type TodoTitle = Pick<Todo, "title">;
Quick Recap
- Classes: Provide clear, reusable code structure.
- Access Modifiers: Enhance encapsulation and visibility control.
- Advanced Types: Unions, intersections, discriminated unions, and mapped types enable sophisticated and flexible architectures.
- Utility Types: Simplify type transformations and definitions, increasing code clarity.
Coming up next: Adopting TypeScript in Your JavaScript Projects—learn practical methods for integrating TypeScript seamlessly, adding types for external libraries, and managing gradual migrations.