instanceof
The instanceof operator tests whether a value is an instance of a given
class, walking the inheritance chain. It returns a bool and — more
importantly — it narrows the type of the left-hand value inside the
if branch.
value instanceof ClassName- Left-hand side: any expression.
- Right-hand side: a bare class identifier (not a
dataType, not an enum). - Result:
trueifvalueis an instance ofClassNameor any subclass,falseotherwise.
Basic check
Section titled “Basic check”class Animal { public name: string constructor(n: string) { this.name = n }}class Dog extends Animal { public breed: string constructor(n: string, b: string) { super(n); this.breed = b }}
var d = new Dog("Rex", "Lab")
if (d instanceof Dog) { println("is a Dog") } // is a Dogif (d instanceof Animal) { println("is an Animal") } // is an Animal — inheritanceinstanceof walks the parent chain, so d instanceof Animal is true
because Dog extends Animal.
Narrowing in the consequent
Section titled “Narrowing in the consequent”Inside an if (x instanceof C) branch, x is narrowed to C. Subclass
properties become directly accessible without a cast:
unionType Shape = Circle | Rectangle
class Circle { public radius: float }class Rectangle { public width: float; public height: float }
function area(s: Shape): float { if (s instanceof Circle) { return 3.14159 * s.radius * s.radius // s is Circle here } return s.width * s.height // s is Rectangle here}In the else path (or after a guard return), the remaining union members
are what’s left — a Shape that is not a Circle must be a Rectangle.
Negation
Section titled “Negation”Prefix the check with ! to narrow the else branch:
function name(s: Shape): string { if (!(s instanceof Circle)) { // s narrowed to Rectangle return "rect " + string(s.width) + "x" + string(s.height) } return "circle r=" + string(s.radius)}Guard-style early returns
Section titled “Guard-style early returns”instanceof works with the same narrowing flow as == null guards:
function describe(s: Shape): string { if (!(s instanceof Circle)) { return "rect" } // After the guard, s is narrowed to Circle for the rest of the function. return "circle r=" + string(s.radius)}With generic classes
Section titled “With generic classes”instanceof matches the base class name, so it works across all
specialisations of a generic class:
class Box<T> { public value: T; constructor(v: T) { this.value = v } }
var b: any = new Box<int>(42)if (b instanceof Box) { // b is a Box (but the specific type argument is not recovered). println("is a Box")}What’s not supported
Section titled “What’s not supported”-
RHS must be a class.
value instanceof MyInterface,value instanceof MyEnum, andvalue instanceof MyDataTypeare compile errors. Use interface methods ortypeofnarrowing instead. -
RHS must be an identifier. Expressions like
x instanceof (cond ? A : B)are rejected. -
LHS must be a simple identifier for narrowing.
(obj.field instanceof Foo)still returns the rightbool, but the typechecker does not narrowobj.field. Assign to a local first:var inner = obj.fieldif (inner instanceof Foo) { /* inner narrowed */ }
How it works
Section titled “How it works”- VM: walks the class’s
.Superchain comparing names. - AOT: uses reflection to read the concrete struct name, then
walks a compile-time emitted
__chuks_class_parentsmap.
Both backends produce identical observable behaviour.
See also
Section titled “See also”- Union Types — the most common place you’ll reach
for
instanceof. - Classes & OOP — inheritance and
extends. - Type narrowing —
== nullandtypeofnarrowing.