Classes

This section documents the current TypeScript release line as published at the official TypeScript documentation, which is the reference these pages are written and verified against. No specific patch version is pinned.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since TypeScript iterates quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

TypeScript classes are ES2015 classes (see Classes) plus a type layer: member type annotations, visibility modifiers, abstract, implements, and override checking. Most of that layer is erased at compile time, but parameter properties, #private members and static blocks emit real runtime code. This page follows the handbook’s Classes chapter.

Fields, methods and accessors

Fields carry a type; methods and get/set accessors are annotated like functions. A field with an initializer infers its type from that initializer.

class Circle {
  radius: number;
  readonly kind = "circle";   // literal type "circle", cannot be reassigned
  label?: string;             // optional: type is string | undefined
  constructor(radius: number) {
    this.radius = radius;
  }
  area(): number {
    return Math.PI * this.radius ** 2;
  }
  get diameter(): number {
    return this.radius * 2;
  }
  set diameter(value: number) {
    this.radius = value / 2;
  }
}

With strict on — specifically strictPropertyInitialization — every non-optional field must be assigned in its declaration or in the constructor. When a field is really initialized elsewhere (a lifecycle hook, a shared init() helper), the definite-assignment assertion !: tells the compiler to trust you.

class Config {
  settings!: Record<string, string>;   // "trust me, this is assigned before use"
  constructor() {
    this.settings = load();
  }
}

Visibility: erased modifiers vs. hard privacy

public (the default), protected and private are compile-time only. They are checked and then erased — at runtime the properties are ordinary and reachable through (obj as any).x. The ECMAScript #name form is real: the JavaScript runtime itself forbids access from outside the class body.

class Account {
  public owner: string;
  protected balance = 0;
  private pin: string;
  #token = crypto.randomUUID();   // genuinely private at runtime
  constructor(owner: string, pin: string) {
    this.owner = owner;
    this.pin = pin;
  }
  sameToken(other: Account): boolean {
    return this.#token === other.#token;   // OK: still inside the class body
  }
}

const a = new Account("Ada", "0000");
a.balance;        // Error: 'balance' is protected
a.pin;            // Error: 'pin' is private
(a as any).pin;   // compiles and works -- 'private' was erased
// a.#token;      // SyntaxError at parse time -- hard privacy

Prefer #private when the boundary must hold at runtime; the private keyword is fine for plain intra-team encapsulation and is friendlier to tests and debuggers.

static members and static blocks

static attaches a member to the class object rather than to instances. A static block runs once when the class is initialized and can see private static state.

class Registry {
  static readonly items: string[] = [];
  static #count = 0;
  static {
    Registry.items.push("default");
    Registry.#count = Registry.items.length;
  }
  static get count(): number {
    return Registry.#count;
  }
}

abstract classes and members

An abstract class cannot be instantiated directly. abstract methods and fields have no body and must be implemented by concrete subclasses; concrete methods on the abstract class may call the abstract ones.

abstract class Shape {
  abstract area(): number;
  describe(): string {
    return `area is ${this.area().toFixed(2)}`;
  }
}

class Square extends Shape {
  constructor(private readonly side: number) {
    super();
  }
  area(): number {
    return this.side ** 2;
  }
}

new Shape();   // Error: cannot create an instance of an abstract class

Parameter properties

Declaring a constructor parameter with a visibility modifier (or readonly) both takes the argument and declares-and-assigns a field of the same name. constructor(private readonly x: number) replaces a field declaration, a parameter, and a this.x = x line. See Parameter Properties.

class Vector {
  constructor(
    public readonly x: number,
    public readonly y: number,
  ) {}
  plus(other: Vector): Vector {
    return new Vector(this.x + other.x, this.y + other.y);
  }
}

new Vector(1, 2).plus(new Vector(3, 4));   // Vector { x: 4, y: 6 }

Unlike type annotations, parameter properties emit runtime code — the compiler generates the field assignments — so they are not erasable syntax. A file that uses them cannot be handled by a pure type-stripping step (--erasableSyntaxOnly, Node’s built-in type stripping, some fast loaders); write the field and the assignment out by hand if every construct must be erasable.

implements vs. extends, and override

extends inherits implementation from one base class. implements only adds a check that the class is assignable to one or more interfaces — it contributes nothing to the body and does not infer or fill in members.

interface Serializable {
  toJSON(): string;
}

class Session extends EventTarget implements Serializable {
  constructor(private readonly id: string) {
    super();
  }
  toJSON(): string {
    return JSON.stringify({ id: this.id });
  }
}

When overriding an inherited member, mark it override. With noImplicitOverride on, redefining an inherited member without override is an error, and an override member whose base member later disappears is flagged too — both directions stay honest as the base class evolves.

class Base {
  greet(): string {
    return "hi";
  }
}

class Loud extends Base {
  override greet(): string {
    return super.greet().toUpperCase();
  }
}

A class is both a value and a type

Declaring class Duration introduces a type named Duration (the instance shape) and a value named Duration (the constructor object). typeof Duration is the constructor’s type.

class Duration {
  constructor(public readonly ms: number) {}
}

const d: Duration = new Duration(1000);   // 'Duration' as a type
const ctor = Duration;                    // 'Duration' as a value
type Ctor = typeof Duration;              // new (ms: number) => Duration

this types and polymorphic this

Inside a class, this is a type that resolves to the actual subclass at each call site. Returning this from a builder method keeps the fluent chain typed as the most-derived class.

class QueryBuilder {
  protected parts: string[] = [];
  where(clause: string): this {
    this.parts.push(clause);
    return this;
  }
}

class SqlQueryBuilder extends QueryBuilder {
  limit(n: number): this {
    this.parts.push(`LIMIT ${n}`);
    return this;
  }
}

new SqlQueryBuilder().where("a = 1").limit(10);   // still SqlQueryBuilder, so .limit() stays visible

Generic classes and mixins

Classes take type parameters just like functions: class Box<T>. The full treatment — constraints, defaults, const type parameters, inference — is on Generics.

class Box<T> {
  constructor(public value: T) {}
  map<U>(f: (value: T) => U): Box<U> {
    return new Box(f(this.value));
  }
}

new Box(2).map((n) => n.toString());   // Box<string>

The mixin pattern composes reusable slices of behavior with functions that take a class and return an extended subclass, using a generic constructor type as the constraint. See the handbook’s Mixins page.

type Constructor<T = object> = new (...args: any[]) => T;

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    createdAt = Date.now();
  };
}

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

const TimestampedUser = Timestamped(User);
const u = new TimestampedUser("Ada");
u.name;        // from User
u.createdAt;   // from the mixin

For adding behavior through class and member decorators instead, see Decorators and Metadata.

See also

  • Classes — the ES2015 class runtime that TypeScript classes compile to.

  • Generics — type parameters, constraints, and the constructor types mixins depend on.

  • Objects and Interfaces — interfaces, implements targets, and structural typing.

  • Decorators and Metadata — the standard (TC39) decorator syntax for classes and members.