Learn TypeScript - Classes, Inheritance, Modifiers, and Abstraction
Episode 9 of 23

Learn TypeScript - Classes, Inheritance, Modifiers, and Abstraction

This episode covers class typing in TypeScript: property declarations, parameter properties, inheritance, access modifiers, readonly, and abstract classes with implements. You'll also see when OOP is the right choice.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A class in TypeScript is a JavaScript class strengthened by the type system. On top of standard class syntax, TypeScript adds property declarations that must be initialized, access modifiers enforced by the compiler, and the ability to mark members abstract. The result is OOP you can rely on for complex domain models.

Some of the modern community prefers functions and plain objects. Yet classes remain the primary choice in many codebases, especially those interacting with object-oriented frameworks or needing a clear behavioral interface.

Episode 9 dissects how to write typed classes, inherit from other classes, control member visibility, and design abstractions with abstract classes and interfaces.

Writing Typed Classes

Properties and Constructors

Properties must be declared and initialized before use:

Class dasar
class Akun {
    nama: string;
    saldo: number;
 
    constructor(nama: string, saldo: number) {
        this.nama = nama;
        this.saldo = saldo;
    }
 
    simpan(amount: number): void {
        this.saldo += amount;
    }
}

The declarations nama: string and saldo: number are property contracts. The constructor accepts typed arguments and copies them into the properties. The simpan method modifies the balance with a number argument. TypeScript ensures no property is used before it's initialized.

Parameter Properties

TypeScript offers a concise way to declare properties directly from constructor parameters:

Parameter properties
class Akun {
    constructor(
        public nama: string,
        private saldo: number = 0,
    ) {}
 
    tampilkanSaldo(): number {
        return this.saldo;
    }
}

A parameter given a modifier like public or private automatically becomes a property. The code above is equivalent to declaring properties and copying them manually in the constructor, but far more concise.

Inheritance

A class can inherit properties and methods from another class:

Inheritance
class Tabungan extends Akun {
    private bunga: number;
 
    constructor(nama: string, saldo: number, bunga: number) {
        super(nama, saldo);
        this.bunga = bunga;
    }
 
    tambahBunga(): void {
        const nilai = (this.saldo * this.bunga) / 100;
        this.simpan(nilai);
    }
}

The extends keyword gives Tabungan everything Akun has. A subclass constructor must call super before touching this. Methods can be overridden to adjust behavior, and TypeScript checks type compatibility during overriding.

Access Modifiers

public, private, and protected

Modifiers control who may access a member:

Access modifiers
class Rekening {
    public pemilik: string;
    protected history: string[] = [];
    private pin: number;
 
    constructor(pemilik: string, pin: number) {
        this.pemilik = pemilik;
        this.pin = pin;
    }
}

public is accessible from anywhere and is the default. protected is accessible only from inside the class and its subclasses. private is accessible only from inside the same class. These modifiers prevent use of internal members that should stay hidden, fully checked by the compiler.

readonly Properties

Properties that must not change after initialization are marked readonly:

Readonly property
class Transaksi {
    readonly id: string;
 
    constructor(id: string) {
        this.id = id;
    }
}

The id property can only be assigned at declaration or in the constructor. After that, rewriting it is an error. readonly can be combined with access modifiers like private readonly.

Abstract Classes and implements

Abstract Classes

An abstract class is a blueprint that can't be instantiated directly:

Abstract class
abstract class Pembayaran {
    abstract jumlah(): number;
 
    deskripsi(): string {
        return `Total: ${this.jumlah()}`;
    }
}
 
class Kartu extends Pembayaran {
    jumlah(): number {
        return 50_000;
    }
}

The method abstract jumlah() is declared without a body; every subclass must implement it. Concrete methods like deskripsi can call abstract methods. This pattern forces consistent structure without copying implementations.

implements for Contracts

A class can also promise to satisfy an interface shape with implements:

implements interface
interface Penyimpan {
    simpan(data: string): void;
}
 
class FilePenyimpan implements Penyimpan {
    simpan(data: string): void {
        console.log(`Menulis: ${data}`);
    }
}

implements forces the class to provide every member the interface asks for. Unlike extends, there's no inheritance of implementation, only a structural check.

Warning

Classes give convenient structure, but they aren't always the answer. For plain data and transformations, ordinary objects with functions are simpler. Choose classes when you need state encapsulation, behavioral polymorphism, or to satisfy an interface from a library.

Closing

Episode 9 equips you with fully typed OOP: guaranteed properties, concise parameter properties, inheritance with type checking, access modifiers, and abstraction through abstract classes and interfaces.

Key takeaways:

  • Class properties must be declared and initialized.
  • Parameter properties shorten property declarations from the constructor.
  • extends inherits behavior; subclasses must call super.
  • public is open, protected for subclasses, private for the class itself.
  • readonly locks a property after initialization.
  • Abstract classes provide a blueprint; implements enforces an interface contract.

In the next episode 10 we'll discuss namespaces, modules, and type-only imports and exports — how to organize code and types into isolated units that are easy to import.