External Publication
Visit Post

TypeScript. Object-oriented programming (OOP)

DEV Community [Unofficial] June 19, 2026
Source

When we hear about OOP, what is the first thing that comes to mind? Classes!

Basic structure

Classes in TypeScript look pretty the same as in JavaScript only with specifying types.

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  greet() {
    console.log(`Welcome, ${this.name}!`);
  }
}

const p1 = new Person("Mary");

p1.greet(); // Welcome, Mary!

Access modifiers

We can restrict or open access to specific properties or methods by using keywords:

Private modifier

The private modifier makes a property available only within the class.

class Person {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
  greet() {
    console.log(`Welcome, ${this.name}!`);
  }
}

const p1 = new Person("Mary");

console.log(p1.name); // Property 'name' is private and only accessible within class 'Person'

But how would we read it ouside the class? Here comes getter :

class Person {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
  greet() {
    console.log(`Welcome, ${this.name}!`);
  }
  getName() {
    return this.name;
  }
}

const p1 = new Person("Mary");

console.log(p1.getName()); // Mary

We can also use setter to change the private property (we can also set some conditions):

class Person {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
  greet() {
    console.log(`Welcome, ${this.name}!`);
  }
  setName(name: string) {
    if (name.length < 5) return;
    this.name = name;
  }
}

Public modifier

Actually, this value is there by default.

class Person {
  public name: string;
  constructor(name: string) {
    this.name = name;
  }
  greet() {
    console.log(`Welcome, ${this.name}!`);
  }
}

const p1 = new Person("Mary");

console.log(p1.name); // Mary

Protected modifier

The protected modifier makes a property available only within the class and its subclasses:

class Person {
  protected name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class User extends Person {
  greet() {
    console.log(`Hello there, ${this.name}`);
  }
}

const p1 = new User("Mary");

console.log(p1.greet());

Readonly modifier

The readonly modifier prevents the property from being modified outside of the constructor:

class Person {
  readonly name: string = "No name";
  constructor(otherName: string) {
    this.name = otherName;
  }
  changeName(otherName: string) {
    this.name = otherName; // Cannot assign to 'name' because it is a read-only property
  }
}

const p1 = new Person("Mary");
p1.name = "Ann"; // Cannot assign to 'name' because it is a read-only property

We can also use it for interfaces:

interface User {
  readonly password: string;
  name: string;
}

let user: User = {
  password: 'password',
  name: 'John Smith'
}

user.name = 'Mary Smith';

user.password = 'newPassword'; //Cannot assign to 'password' because it is a read-only property.

Abstract class

This is a restricted class (we can't create instances from it), from which we can create subclasses. It's usually used to define mandatory methods.

abstract class Dog {
  abstract bark(duration: number): void;

  walk(duration: number) {
    console.log("Walking");
    this.bark(duration);
  }
}

class Husky extends Dog {
  bark(duration: number) {
    console.log("Wooooooo");
  }
}

class Chihuahua extends Dog {
  bark(duration: number) {
    console.log("wof wof wof");
  }
}

let d1 = new Husky();

d1.walk(2);

Here, subclasses must implement their own bark method, since it's defined with keywork abstract. They inherited the method walk.

Classes and interfaces

Classes can implement an interface, that allows to treat instances from different classes (that implement the same interface) as the same object hiding complexity, that we don't care about it at this moment:

interface MakeSound {
  makeSound(): void;
}

class Python implements MakeSound {
  length: number;

  constructor(length: number) {
    this.length = length;
  }

  makeSound() {
    console.log('Ssssss!');
  }
}

class Puma implements MakeSound {
  makeSound() {
    console.log('Roar!');
  }
}

const python = new Python(10);
const puma = new Puma();

function animalSpeak(animal: MakeSound) {
  animal.makeSound();
}

animalSpeak(python);
animalSpeak(puma);

function animalSpeak is interested only in the ability of object to makeSound , it doesn't care if the object is python or puma.

Static method and field

Like in JavaScript, we can define static methods and fields for a class. They cannot be accessed on instances, but on the class itself. We can use it to create some shared values or maybe count instances that exist:

class Person {
  static instanceCount: number = 0;
  name: string;

  constructor(name: string) {
    Person.instanceCount++;
    this.name = name;
  }
}

const p1 = new Person('Mary');
const p2 = new Person('Tom');
console.log(Person.instanceCount); // 2

We also can define static method:

class Person {
  static instanceCount: number = 0;
  name: string;

  constructor(name: string) {
    Person.instanceCount++;
    this.name = name;
  }

  static clearCount() {
    this.instanceCount = 0;
  }
}

const p1 = new Person('Mary');
const p2 = new Person('Tom');
Person.clearCount();
console.log(Person.instanceCount); // 0

Static methods can only access variables that associate with the class.

Discussion in the ATmosphere

Loading comments...