Inheritance
A class can build on another with extends, inheriting its fields and methods while adding or
overriding behavior. Zeus uses single inheritance with dynamic dispatch — a method call
through a base-typed reference always runs the most-derived override.
Extending a class
Use extends to derive one class from another. The derived class inherits every field and method
of its base:
class Counter { public count: i32; constructor() { this.count = 0; }
public bump(): i32 { this.count = this.count + 1; return this.count; }}
class Fancy extends Counter { public tag: i32; constructor() { super(); // run the base constructor first this.count = 41; // then set the inherited field this.tag = 7; // ...and the class's own field }}
let f: Fancy = new Fancy();console.log(f.bump()); // base method mutates the inherited field => 42Fancy carries the inherited count field and bump method alongside its own tag field. A
base method operates on the inherited field even when called on a derived instance.
Overriding methods
A derived class replaces an inherited method by declaring one with the same name. Overrides are dispatched dynamically: the object’s actual class decides which implementation runs, even through a base-typed reference.
class Animal { public sound(): i32 { return 0; }}
class Dog extends Animal { public sound(): i32 { return 42; }}
let a: Animal = new Dog();console.log(a.sound()); // Dog.sound() via dynamic dispatch => 42Polymorphism
Because a derived instance is a base instance, you can pass it wherever the base type is expected — an upcast. Virtual methods still dispatch to the derived override:
class Shape { public area(): i32 { return 0; }}
class Square extends Shape { public s: i32; constructor(s: i32) { this.s = s; } public area(): i32 { return this.s * this.s; }}
function describe(sh: Shape): i32 { return sh.area(); // runs the concrete override}
let sq: Square = new Square(6);console.log(describe(sq) + 6); // 36 + 6 => 42Calling the base with super
Inside an override, super.method() calls the base implementation, letting you extend inherited
behavior instead of replacing it. A super call is non-virtual — it always runs the base
version, never the override:
class Animal { public sound(): i32 { return 10; }}
class Dog extends Animal { public sound(): i32 { return super.sound() + 32; }}
let a: Animal = new Dog();console.log(a.sound()); // Dog.sound (virtual) -> super.sound (base) + 32 => 42Constructors and super(...)
When a derived class defines its own constructor, it calls the base constructor with super(...)
to initialize the inherited fields:
class Animal { public legs: i32; constructor(legs: i32) { this.legs = legs; }}
class Dog extends Animal { public tail: i32; constructor(legs: i32, tail: i32) { super(legs); // initialize the base part first this.tail = tail; // then the derived part }}
let d: Dog = new Dog(40, 2);console.log(d.legs + d.tail); // 42Inherited constructors
A derived class with no constructor of its own inherits the base constructor. new Derived(args)
forwards straight to it:
class Base { public v: i32; constructor(v: i32) { this.v = v; }}
class Derived extends Base { }
let d: Derived = new Derived(42);console.log(d.v); // 42Multi-level inheritance
Inheritance chains any number of levels deep. Overrides and inherited members resolve through the whole chain:
class A { public who(): i32 { return 1; } public gift(): i32 { return 40; }}
class B extends A { public who(): i32 { return 2; } // override}
class C extends B { } // inherits B.who and A.gift
let c: A = new C();console.log(c.who() + c.gift()); // 2 (from B) + 40 (from A) => 42Constructors chain the same way — each level calls super(...) on the level above it. Static
members are shared down the chain too; see Static Members.