LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 817 min read

Classes & OOP

Learn TypeScript classes: access modifiers, readonly and parameter properties, inheritance, abstract classes, interfaces, and getters/setters.

Introduction

JavaScript classes already exist; TypeScript layers access control, parameter shorthand, abstract classes, and interface enforcement on top of them, making object-oriented code far more self-documenting and safe to refactor.

What You Will Learn
  • How to define a class with typed fields, a constructor, and methods.
  • The public, private, and protected access modifiers.
  • Parameter properties — a shorthand for declaring and assigning fields in the constructor.
  • How inheritance works with extends and super.
  • Abstract classes and implementing interfaces in a class.

Class Basics

A class declares typed fields, a constructor to initialize them, and methods that operate on the instance. TypeScript checks that every field is assigned a value of the correct type.

class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
}
const p = new Person("Amol", 29);
console.log(p.greet());
Output

Click Run to see what this code prints.

Access Modifiers

TypeScript adds `public` (the default — accessible from anywhere), `private` (accessible only inside the declaring class), and `protected` (accessible inside the declaring class and its subclasses). These are checked only by the compiler; the compiled JavaScript output still contains a normal, accessible property. For genuine runtime privacy, JavaScript's own `#fieldName` syntax should be used instead.

class BankAccount {
private balance: number = 0;
deposit(amount: number): void {
this.balance += amount;
}
getBalance(): number {
return this.balance;
}
}

Parameter Properties

Adding an access modifier directly in front of a constructor parameter is shorthand that both declares the field and assigns it — no need to repeat the field declaration and the `this.x = x` assignment separately.

class Product {
constructor(
public readonly id: number,
private price: number
) {}
getPrice(): number {
return this.price;
}
}

Inheritance

A class can extend a base class with `extends`, inheriting its fields and methods and optionally overriding them. Inside a subclass constructor, `super(...)` must be called before `this` can be used, to run the base class's own constructor first.

class Animal {
constructor(protected name: string) {}
speak(): string {
return `${this.name} makes a sound.`;
}
}
class Dog extends Animal {
speak(): string {
return `${this.name} barks.`;
}
}
console.log(new Dog("Rex").speak());
Output

Click Run to see what this code prints.

Abstract Classes

An `abstract class` cannot be instantiated directly — it exists only to be extended. It can mix concrete methods (with a real implementation) with `abstract` methods (a signature every subclass must implement).

abstract class Shape {
abstract area(): number;
describe(): string {
return `Area: ${this.area()}`;
}
}
class Square extends Shape {
constructor(private side: number) {
super();
}
area(): number {
return this.side ** 2;
}
}

Implementing Interfaces

A class can implement one or more interfaces with `implements`, and TypeScript verifies every required property and method is actually present. Unlike `extends` (single inheritance from one base class), a class may implement any number of interfaces at once.

interface Printable {
print(): void;
}
class Invoice implements Printable {
print(): void {
console.log("Printing invoice...");
}
}

Getters and Setters

`get` and `set` accessors let a class expose computed or validated properties that look like plain fields from the outside, while running real code behind the scenes on every read or write.

class Temperature {
private _celsius = 0;
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
set fahrenheit(value: number) {
this._celsius = (value - 32) * 5 / 9;
}
}

Common Mistakes

Avoid These Mistakes
  • Relying on TypeScript's `private` for real runtime privacy — it is erased at compile time; the field is still an ordinary accessible property in the emitted JavaScript.
  • Forgetting to call `super()` before using `this` in a subclass constructor, which is a compile error.
  • Confusing abstract classes (can contain real implementation, single inheritance) with interfaces (pure contracts, multiple implements allowed).
  • Building deep inheritance hierarchies when a simpler interface-plus-composition design would be easier to change later.

Best Practices

  • Prefer composition and interfaces over deep, multi-level inheritance chains.
  • Use parameter properties to cut constructor boilerplate for simple data-holding classes.
  • Mark fields `readonly` whenever they are set once in the constructor and never reassigned afterward.
  • Use JavaScript's native `#privateField` syntax, not TypeScript's `private` keyword, when true runtime privacy actually matters.

Frequently Asked Questions

No — it is only enforced by the compiler. The compiled JavaScript field remains a normal, publicly accessible property. Use the native #field syntax for genuine runtime privacy.

Yes — a class can implement any number of interfaces with implements, separated by commas.

No — JavaScript and TypeScript only support single inheritance via extends. Use interfaces or composition to combine behavior from multiple sources.

An abstract class can contain real, shared implementation and is extended by one subclass hierarchy at a time; an interface is a pure shape contract that any number of unrelated classes can implement.

Key Takeaways

  • Classes combine typed fields, a constructor, and methods, checked fully by the compiler.
  • public, private, and protected control compile-time access, but are erased at runtime.
  • Parameter properties are shorthand for declaring and assigning constructor fields in one step.
  • Abstract classes provide shared implementation and force subclasses to implement specific methods.
  • A class can implement multiple interfaces but extend only one base class.

Summary

You can now build well-structured, type-safe object-oriented code with TypeScript classes. Next, you'll learn generics — the tool that lets you write reusable functions and classes that stay fully type-safe across many different types.

Next Lesson →

Generics