scmJS docs

Classes

Classes are TypeScript's, declared inside the program or at the top of any of the script's files. A method is a function that is handed the instance, so everything on Functions holds for methods too.

program(() => {
  class Wave {
    static made = 0;
    left: number;
    constructor(public unit: UnitType, public count: number) { this.left = count; Wave.made++; }
    spawn(at: Location) { if (this.left > 0) { createUnit(P2, this.unit, 1, at); this.left--; } }
    get done() { return this.left == 0; }
  }
  class Boss extends Wave {
    constructor(unit: UnitType) { super(unit, 1); }
    spawn(at: Location) { super.spawn(at); displayText("The boss is here"); }
  }
  const waves = [new Wave(units.ZergZergling, 12), new Wave(units.ZergHydralisk, 6)];
  const boss = new Boss(units.ZergUltralisk);
  while (!waves.every((w) => w.done)) {
    for (const w of waves) w.spawn(locations.Gate);
    sleep(seconds(1));
  }
  boss.spawn(locations.Gate);
});

Fields with their first values, constructors and constructor(public x: number), methods, get and set, static, readonly, private, protected and #x, extends with super, abstract, implements and type parameters all work. A field holds what a record's does, and also another instance (pos = new Vec(0, 0)). this.members = [] starts an array over.

How an instance is kept#

The compiler keeps an instance one of two ways:

class Enemy {
  constructor(public hp: number) {}
  worth() { return 1; }
}
class Boss extends Enemy {
  worth() { return 10; }
}

program(() => {
  const foes: Enemy[] = [new Enemy(5), new Boss(50), new Enemy(5)];
  let target: Enemy | null = null;
  let score = 0;
  for (const f of foes) {
    score += f.worth();
    if (!target || f.hp > target.hp) target = f;
  }
  print(`${score} ${target?.hp ?? 0}`);         // 12 50
});

Two limits: