Skip to content
Classes with Access Modifiers
step 1/6

Reading — step 1 of 6

Learn

~2 min readClasses and Modules

TypeScript adds access modifiers and other class features on top of JavaScript classes — making OOP-style code work the way you expect.

Access modifiers

class BankAccount {
    public owner: string;
    private balance: number;
    protected accountNumber: string;
    readonly createdAt: Date;
    
    constructor(owner: string, accountNumber: string) {
        this.owner = owner;
        this.accountNumber = accountNumber;
        this.balance = 0;
        this.createdAt = new Date();
    }
    
    deposit(amount: number): void {
        if (amount > 0) this.balance += amount;
    }
    
    getBalance(): number {
        return this.balance;
    }
}
  • public (default) — accessible everywhere.
  • private — only within this class.
  • protected — within this class and subclasses.
  • readonly — can be set in the constructor but not reassigned later.

At RUNTIME, only #name (real private) gives true encapsulation. private is enforced at compile time only — the JS output is just a normal property.

Parameter properties — shortcut

Declare and assign in one line:

class Point {
    constructor(
        public readonly x: number,
        public readonly y: number,
    ) {}
}

const p = new Point(3, 4);
console.log(p.x);   // 3

The public readonly in the constructor parameter both declares the field AND assigns it. Saves the boilerplate of this.x = x; this.y = y;.

Inheritance and super

class Animal {
    constructor(public name: string) {}
    
    describe(): string {
        return `${this.name} is an animal`;
    }
}

class Dog extends Animal {
    constructor(name: string, public breed: string) {
        super(name);
    }
    
    describe(): string {
        return `${this.name} is a ${this.breed}`;
    }
    
    parentDescribe(): string {
        return super.describe();    // call parent's version
    }
}

Abstract classes

Classes that can't be instantiated directly — used as base templates:

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; }
}

// const s = new Shape();    // ✗ compile error
const c = new Circle(5);
console.log(c.describe());

abstract methods have no body — subclasses must implement them.

Implementing interfaces

interface Greetable {
    greet(): string;
}

class User implements Greetable {
    constructor(private name: string) {}
    greet(): string { return `Hi, ${this.name}`; }
}

implements enforces the shape at compile time. Multiple interfaces are allowed: class X implements A, B, C.

Static members

Attached to the class, not instances:

class IDGenerator {
    private static nextId = 0;
    static getId(): number {
        return ++IDGenerator.nextId;
    }
}

IDGenerator.getId();   // 1
IDGenerator.getId();   // 2

Common mistakes

  • Confusing private with #private#name is real private (enforced at runtime); private name is compile-time only.
  • Forgetting super() in subclass constructor — required before using this.
  • Using readonly for deep immutability — only locks the property reference, not the object's contents.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…