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 }}
function main(): i32 { let f: Fancy = new Fancy(); return f.bump(); // base method mutates the inherited field => 42}Fancy 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; }}
function main(): i32 { let a: Animal = new Dog(); return a.sound(); // Dog.sound() via dynamic dispatch => 42}Polymorphism
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}
function main(): i32 { let sq: Square = new Square(6); return describe(sq) + 6; // 36 + 6 => 42}Calling 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; }}
function main(): i32 { let a: Animal = new Dog(); return a.sound(); // Dog.sound (virtual) -> super.sound (base) + 32 => 42}Constructors 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 }}
function main(): i32 { let d: Dog = new Dog(40, 2); return d.legs + d.tail; // => 42}Inherited 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 { }
function main(): i32 { let d: Derived = new Derived(42); return d.v; // => 42}Multi-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
function main(): i32 { let c: A = new C(); return c.who() + c.gift(); // 2 (from B) + 40 (from A) => 42}Constructors chain the same way — each level calls super(...) on the level above it. Static
members are shared down the chain too; see Static Members.