Skip to content

Classes

A class defines a custom type with fields (data) and methods (behavior). This page covers defining and using classes; the rest of this section goes deeper: Encapsulation & Accessors for access control, Static Members for class-level state, and Inheritance for subclassing.

Defining a class

Declare fields with a type, then create instances with new:

class Rectangle {
public width: f64;
public height: f64;
}
let rect: Rectangle = new Rectangle();
rect.width = 10.0;
rect.height = 5.0;

Constructors

A constructor initializes an object’s fields. It runs automatically when you write new ClassName(...):

class Point {
public x: i32;
public y: i32;
constructor(x: i32, y: i32) {
this.x = x;
this.y = y;
}
}
let p: Point = new Point(10, 20);
console.log(p.x + p.y); // 30

Methods

Methods are functions attached to a class that operate on the instance:

class Rectangle {
public width: f64;
public height: f64;
constructor(w: f64, h: f64) {
this.width = w;
this.height = h;
}
public area(): f64 {
return this.width * this.height;
}
public perimeter(): f64 {
return 2.0 * (this.width + this.height);
}
}
let rect: Rectangle = new Rectangle(10.0, 5.0);
let a: f64 = rect.area(); // 50.0
let p: f64 = rect.perimeter(); // 30.0

Methods can take rest parameters — prefix the final parameter with ...:

class Accumulator {
public base: i32;
constructor(base: i32) {
this.base = base;
}
public sum(...nums: i32[]): i32 {
let total: i32 = this.base;
for (let i: i32 = 0; i < nums.length; i++) {
total += nums[i];
}
return total;
}
}
let acc: Accumulator = new Accumulator(10);
let total: i32 = acc.sum(1, 2, 3); // 16

String representation

Every value can be turned into a string, so console.log, string concatenation, and template strings work on any object out of the box. By default an object prints structurally (ClassName { field: value }). Define a public toString(): string to customize it — it’s used everywhere the object becomes a string, including nested inside other objects:

class Temperature {
celsius: f64;
constructor(celsius: f64) { this.celsius = celsius; }
toString(): string { return this.celsius + "°C"; }
}
let t: Temperature = new Temperature(21.5);
console.log(t); // 21.5°C
console.log("now: " + t); // now: 21.5°C

See Console for the full details of how values render.

The this keyword

Inside a method, this refers to the current instance — use it to read and update the object’s own fields:

class Counter {
public value: i32;
constructor(start: i32) {
this.value = start;
}
public increment(): void {
this.value = this.value + 1;
}
public getValue(): i32 {
return this.value;
}
}
let c: Counter = new Counter(41);
c.increment();
console.log(c.getValue()); // 42

Object references

Objects are passed by reference. A function that mutates its parameter changes the caller’s object:

class Box {
public value: i32;
constructor(v: i32) { this.value = v; }
}
function modifyBox(box: Box): void {
box.value = 999;
}
let myBox: Box = new Box(10);
modifyBox(myBox);
console.log(myBox.value); // 999

Nested objects

A field can hold another object, letting you compose larger structures:

class Point {
public x: i32;
public y: i32;
constructor(x: i32, y: i32) {
this.x = x;
this.y = y;
}
}
class Line {
public start: Point;
public end: Point;
constructor(p1: Point, p2: Point) {
this.start = p1;
this.end = p2;
}
public length(): i32 {
let dx: i32 = this.end.x - this.start.x;
let dy: i32 = this.end.y - this.start.y;
return dx + dy; // Manhattan distance
}
}
let line: Line = new Line(new Point(0, 0), new Point(10, 20));
console.log(line.length()); // 30

Null and null-safety

An object variable can be null, either explicitly or when left uninitialized:

class Node {
public value: i32;
public next: Node;
constructor(v: i32) {
this.value = v;
this.next = null; // no next node yet
}
}

Zeus checks for null on every property access. Reaching through a null reference throws a NullReferenceException:

let person: Person; // null by default
console.log(person.name); // throws NullReferenceException
function getName(person: Person): string {
try {
return person.name;
} catch (e: Error) {
return "Unknown";
}
}

Classes with arrays

A field can be an array. Initialize it in the constructor, then use the array methods:

class Team {
public scores: i32[];
constructor() {
this.scores = new i32[];
}
public addScore(score: i32): void {
this.scores.push(score);
}
public getScore(index: i32): i32 {
return this.scores.get(index);
}
}
let team: Team = new Team();
team.addScore(10);
team.addScore(20);
team.addScore(30);
console.log(team.getScore(1)); // 20

Anonymous classes

A class expression creates a type inline, without a top-level declaration — handy for one-off objects.

Assign it to a const and use that name as the type:

const Counter = class {
public n: i32;
constructor(start: i32) { this.n = start; }
inc(): i32 {
this.n = this.n + 1;
return this.n;
}
}
let c = new Counter(9);
console.log(c.inc()); // 10

You can also give the expression an internal name — both the variable and the class name are then usable:

const Vec = class Vector {
public x: i32;
public y: i32;
constructor(x: i32, y: i32) {
this.x = x;
this.y = y;
}
sum(): i32 { return this.x + this.y; }
}
let a = new Vec(3, 4); // via the const
let b = new Vector(1, 2); // via the class name
console.log(a.sum() + b.sum()); // 10

Or define and instantiate in a single expression — the class equivalent of an IIFE:

let obj = new (class {
public v: i32;
constructor(v: i32) { this.v = v; }
get(): i32 { return this.v; }
})(42);
console.log(obj.get()); // 42