Difficulty: Intermediate
How do classes work with TypeScript's type system? Explain implements, access modifiers, and parameter properties.
TypeScript enhances JavaScript classes with type annotations, access modifiers, and interface implementation.
Key features: - 'implements' keyword: a class promises to satisfy an interface contract - Access modifiers: public (default), private, protected, and #private (ES native) - Parameter properties: shorthand to declare and assign properties in the constructor - Static members: properties and methods on the class itself, not instances - Structural typing still applies: a class instance satisfies any interface with a matching shape, even without 'implements'
Classes in TypeScript generate both a runtime value (the constructor function) and a type (the instance shape).
interface Serializable {
serialize(): string;
deserialize(data: string): void;
}
interface Loggable {
log(message: string): void;
}
// A class can implement multiple interfaces
class User implements Serializable, Loggable {
constructor(
public id: number,
public name: string,
public email: string
) {}
serialize(): string {
return JSON.stringify({ id: this.id, name: this.name, email: this.email });
}
deserialize(data: string): void {
const parsed = JSON.parse(data);
this.id = parsed.id;
this.name = parsed.name;
this.email = parsed.email;
}
log(message: string): void {
console.log(`[User ${this.id}] ${message}`);
}
}
// Structural typing - no implements needed
const obj = { serialize: () => '{}', deserialize: () => {}, log: () => {} };
const s: Serializable = obj; // OK - has the right shape
implements ensures a class fulfills an interface contract at compile time. However, structural typing means any object with the right shape is compatible.
class BankAccount {
// Parameter properties: declare + assign in constructor
constructor(
public readonly accountNumber: string, // public, readonly
private balance: number, // only accessible inside class
protected currency: string = 'USD' // accessible in subclasses
) {}
// Public method (default)
getBalance(): string {
return `${this.currency} ${this.balance.toFixed(2)}`;
}
// Private method
private validateAmount(amount: number): void {
if (amount <= 0) throw new Error('Amount must be positive');
}
deposit(amount: number): void {
this.validateAmount(amount);
this.balance += amount;
}
withdraw(amount: number): void {
this.validateAmount(amount);
if (amount > this.balance) throw new Error('Insufficient funds');
this.balance -= amount;
}
}
const account = new BankAccount('ACC-001', 1000);
account.getBalance(); // OK: 'USD 1000.00'
account.accountNumber; // OK: readonly public
// account.balance; // Error: private
// account.currency; // Error: protected
// account.accountNumber = 'X'; // Error: readonly
Parameter properties (public/private/protected/readonly in constructor params) are a shorthand that declares and initializes in one place, reducing boilerplate.
class MathUtils {
// Static property
static readonly PI = 3.14159;
// Static method
static circleArea(radius: number): number {
return MathUtils.PI * radius 2;
}
// Static factory method
static fromDegrees(degrees: number): number {
return degrees * (MathUtils.PI / 180);
}
}
MathUtils.PI; // 3.14159
MathUtils.circleArea(5); // 78.5...
// const m = new MathUtils(); // Works but pointless
// A class creates both a value and a type
class Point {
constructor(public x: number, public y: number) {}
}
// 'Point' as a type = instance shape
const p: Point = new Point(1, 2);
const p2: Point = { x: 3, y: 4 }; // OK - structural typing
// 'typeof Point' = the class constructor itself
function createInstance(ctor: typeof Point, x: number, y: number): Point {
return new ctor(x, y);
}
// Generic constructor type
interface Constructor<T> {
new (...args: any[]): T;
}
function create<T>(ctor: Constructor<T>, ...args: any[]): T {
return new ctor(...args);
}
A class name used as a type refers to the instance shape. Use 'typeof ClassName' to refer to the constructor. This distinction is important for factory patterns.
Classes, implements, Access Modifiers, Parameter Properties, Static Members