Classes in TypeScript

intermediate
12 min

Classes in TypeScript

TypeScript classes build on JavaScript classes and add the pieces object-oriented developers expect: access modifiers, abstract classes, interface implementation, and concise constructor syntax. Even if you write mostly functional code, classes appear throughout popular frameworks and libraries, so understanding TypeScript's class features is essential.

Declaring a Class

Fields are declared with types; the constructor initializes them:

typescript
class Account { owner: string; balance: number; constructor(owner: string, openingBalance: number) { this.owner = owner; this.balance = openingBalance; } deposit(amount: number): void { this.balance += amount; } } const acct = new Account("Ada", 100); acct.deposit(50);

With strictPropertyInitialization enabled (part of strict mode), every declared field must be initialized in the constructor or given a default — another whole class of bugs eliminated.

Access Modifiers: public, private, protected

TypeScript adds visibility keywords checked at compile time:

typescript
class BankAccount { private balance = 0; // only this class protected owner: string; // this class and subclasses public readonly id: string; // anyone can read, nobody can reassign constructor(owner: string, id: string) { this.owner = owner; this.id = id; } getBalance(): number { return this.balance; // internal access is fine } } const b = new BankAccount("Grace", "acc-1"); b.balance; // Error: balance is private

Note that private is erased at compile time — at runtime the property still exists. For true runtime privacy, JavaScript's native #field syntax works in TypeScript too and enforces privacy in the emitted code.

Parameter Properties: Less Boilerplate

Declaring and assigning fields in one step is so common that TypeScript has a shorthand — put a modifier on a constructor parameter:

typescript
class User { constructor( public readonly id: number, private passwordHash: string, public name: string = "Anonymous", ) {} } const u = new User(1, "x9f2..."); console.log(u.name); // "Anonymous"

Each modified parameter automatically becomes a class field with that visibility.

Getters, Setters, and Inheritance

typescript
class Temperature { constructor(private celsius: number) {} get fahrenheit(): number { return this.celsius * 1.8 + 32; } set fahrenheit(value: number) { this.celsius = (value - 32) / 1.8; } }

Inheritance uses extends, and super(...) must run before touching this. Mark methods designed for overriding clearly, and enable the noImplicitOverride option so subclasses must write the override keyword — the compiler then catches renamed base methods.

Abstract Classes and implements

An abstract class cannot be instantiated; it defines a partial implementation plus required methods:

typescript
abstract class Shape { abstract area(): number; describe(): string { return "Area: " + this.area(); } } class Circle extends Shape { constructor(private radius: number) { super(); } area(): number { return Math.PI * this.radius ** 2; } }

Separately, a class can promise to match an interface with implements:

typescript
interface Serializable { toJSON(): string; } class Config implements Serializable { toJSON(): string { return "{}"; } }

implements only checks the shape — it adds no runtime behavior. Because TypeScript is structural, any object with a toJSON(): string method satisfies Serializable, class or not.

Key Takeaways

  • Class fields are typed and, under strict mode, must be initialized.
  • public, private, and protected are compile-time checks; #fields give runtime privacy.
  • Parameter properties collapse declare-and-assign boilerplate into the constructor signature.
  • Abstract classes share partial implementations; implements verifies a class matches an interface.
  • Use override (with noImplicitOverride) to make inheritance refactoring-safe.

Next lesson: Generics — writing components that work over many types without losing safety.

Classes in TypeScript - TypeScript | CodeYourCraft | CodeYourCraft