Classes in TypeScript: Typed Fields, Access Modifiers, and More

Learn how TypeScript upgrades JavaScript classes with typed fields, access modifiers, readonly, parameter properties, interfaces, and abstract classes — explained plainly for beginners.

Published September 20, 20268 min readBy ACY Partner Indonesia
Classes in TypeScript cover with a typed class snippet
300 × 250Ad Space AvailablePlace your ad here

If you have written a class in plain JavaScript before, you already know most of what you need. A class is a blueprint: you describe what an object should look like and what it can do, and then you create as many objects from that blueprint as you like. TypeScript does not throw any of that away. It keeps the same syntax and adds one thing on top — types — so the editor can catch mistakes before your code ever runs.

This article walks through what TypeScript adds to a class, one feature at a time. We will start from a familiar JavaScript class, then layer on typed fields, access modifiers, readonly, parameter properties, interfaces, and a quick look at abstract classes. By the end you will be able to read and write a typed class with confidence.

If classes themselves are still new to you, it is worth reading JavaScript Classes first. Everything here assumes you already know the basic shape of a class.

Starting from a plain JavaScript class

Here is an ordinary JavaScript class that represents a user. Nothing special yet.

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  greeting() {
    return `Hi, I'm ${this.name}`;
  }
}

const john = new User("John Doe", "john@example.com");

This works, but JavaScript will happily let you do silly things. You could write new User(42, true) and nobody complains until something breaks later. You could type john.emial (a typo) and get undefined instead of an error. TypeScript exists to close those gaps.

Typed fields

The first thing TypeScript adds is the ability to declare your fields with a type. You list each property at the top of the class, write its name, a colon, and the type it holds.

class User {
  name: string;
  email: string;

  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }

  greeting(): string {
    return `Hi, I'm ${this.name}`;
  }
}

const john = new User("John Doe", "john@example.com");

Two things changed. The fields name and email are now declared as string at the top, and the constructor parameters got types too. The greeting() method also says it returns a string. Now if you call new User(42, true), TypeScript stops you immediately and explains that 42 is not a string.

Declare fields, then assign them

In TypeScript you usually list every field at the top of the class with its type. The constructor still assigns the values, but the declaration is what tells TypeScript the shape of each object. Think of it as a label on the box before you put anything inside.

You can also give a field a default value, and TypeScript will infer its type from that value:

class User {
  name: string = "Anonymous";
  isActive: boolean = true;
}

Here you do not even need to write the types — TypeScript reads "Anonymous" and knows name is a string, and reads true and knows isActive is a boolean.

Access modifiers: public, private, protected

In plain JavaScript, every property on an object is reachable from anywhere. Sometimes that is fine. Often it is not — some details are internal plumbing and you do not want other code poking at them. TypeScript gives you three keywords to control who can touch a field or method.

Modifier Who can access it
public Anyone, from anywhere (this is the default)
private Only code inside the same class
protected Code inside the class and any class that extends it

Here is the same User class with a private field for a password hash, which the outside world should never read directly.

class User {
  public name: string;
  private passwordHash: string;

  constructor(name: string, passwordHash: string) {
    this.name = name;
    this.passwordHash = passwordHash;
  }

  public checkPassword(hash: string): boolean {
    return this.passwordHash === hash;
  }
}

const jane = new User("Jane Doe", "a1b2c3");
jane.name;              // OK — public
jane.checkPassword("x"); // OK — public method
jane.passwordHash;       // Error — passwordHash is private

The last line is the point. Outside the class, passwordHash does not exist as far as TypeScript is concerned. You are forced to go through checkPassword, which is exactly the controlled door you wanted.

protected sits in the middle. A protected member is hidden from the outside world, but a subclass can still use it.

class Account {
  protected balance: number = 0;
}

class SavingsAccount extends Account {
  addInterest(rate: number): void {
    this.balance += this.balance * rate; // allowed: subclass can see protected
  }
}

These checks are compile-time only

private and protected are enforced by TypeScript while you write code, not by the browser at runtime. After your TypeScript is compiled to JavaScript, the field is just a normal property. If you need a value to be truly hidden even at runtime, use JavaScript’s native #private fields instead. For day-to-day app code, the TypeScript modifiers are usually enough.

readonly: set once, never change

Some values should be locked in the moment an object is created — an ID, a creation date, things that simply should not change afterwards. The readonly keyword does that. You can assign the field in the declaration or in the constructor, but nowhere else.

class User {
  readonly id: string;
  name: string;

  constructor(id: string, name: string) {
    this.id = id;     // OK — assigning in the constructor
    this.name = name;
  }
}

const john = new User("u-001", "John Doe");
john.name = "Johnny";   // OK
john.id = "u-002";      // Error — id is readonly

readonly is about intent. It tells anyone reading your code, and TypeScript itself, that this value is fixed. Trying to reassign it is caught before the program runs.

Parameter properties: a handy shortcut

Look back at the typed User class. There was a repetitive pattern: declare a field, accept a parameter with the same name, then copy the parameter into the field. TypeScript offers a shortcut that collapses all three steps into one. If you put an access modifier (or readonly) directly in front of a constructor parameter, TypeScript creates the field and assigns it for you.

class User {
  constructor(
    public name: string,
    private email: string,
    readonly id: string
  ) {}

  greeting(): string {
    return `Hi, I'm ${this.name}`;
  }
}

const jane = new User("Jane Doe", "jane@example.com", "u-002");

That tiny class is equivalent to declaring three fields and assigning each one in the constructor body. The constructor body is even empty here because there is nothing left to do. This is called a parameter property, and you will see it often in real TypeScript code.

The modifier is what makes it a field

A constructor parameter only becomes a class field when it has a modifier in front of it — public, private, protected, or readonly. A plain constructor(name: string) with no modifier is just a normal parameter that disappears when the constructor finishes.

Implementing an interface

An interface is a description of a shape: a list of properties and methods that something must have, with no actual code inside. Classes can promise to follow an interface using the implements keyword. TypeScript then checks that the class really does provide everything the interface requires.

interface Greetable {
  name: string;
  greeting(): string;
}

class User implements Greetable {
  constructor(public name: string) {}

  greeting(): string {
    return `Hi, I'm ${this.name}`;
  }
}

If you forgot to write the greeting method, or spelled it wrong, TypeScript would refuse to compile and tell you the class does not match Greetable. This is great for keeping a team honest: agree on an interface, and every class that implements it is guaranteed to have the same shape.

A single class can implement more than one interface — just separate them with commas: class User implements Greetable, Serializable {}.

Abstract classes, briefly

Sometimes you want a base class that should never be created on its own, only extended. An abstract class is exactly that — a partial blueprint. It can contain finished methods, but it can also declare abstract methods that have no body, forcing every subclass to fill them in.

abstract class Shape {
  abstract area(): number; // no body — subclasses must provide one

  describe(): string {
    return `This shape has an area of ${this.area()}`;
  }
}

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }

  area(): number {
    return Math.PI * this.radius ** 2;
  }
}

const c = new Circle(5);
c.describe();      // "This shape has an area of 78.53..."
new Shape();       // Error — cannot create an instance of an abstract class

Shape gives you a shared describe() method for free, but it refuses to be built directly and demands that each subclass define area(). That is the whole idea: shared behaviour up top, required gaps that children must fill.

Quick recap

TypeScript does not reinvent classes — it adds a layer of safety on top of the JavaScript classes you may already know. Here is the short version of everything we covered:

  • Typed fields declare what type each property holds, so wrong values are caught early.
  • Access modifiers (public, private, protected) control who can reach a field or method.
  • readonly locks a value after it is set, usually in the constructor.
  • Parameter properties let you declare and assign a field straight from a constructor parameter by adding a modifier in front of it.
  • implements makes a class promise to match an interface, and TypeScript verifies it.
  • Abstract classes are base blueprints that cannot be instantiated and can force subclasses to implement certain methods.

None of this changes how your code runs in the browser — these checks happen while you write, then melt away into ordinary JavaScript. The payoff is that a whole category of bugs gets caught before you ever hit refresh. Start by adding types to your fields, and bring in the rest as you need it.

Tags:typescriptclassesoopaccess-modifiersinterfacesfrontend
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read