Skip to content

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: true if value is an instance of ClassName or any subclass, false otherwise.
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 Dog
if (d instanceof Animal) { println("is an Animal") } // is an Animal — inheritance

instanceof walks the parent chain, so d instanceof Animal is true because Dog extends Animal.

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.

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)
}

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)
}

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")
}
  • RHS must be a class. value instanceof MyInterface, value instanceof MyEnum, and value instanceof MyDataType are compile errors. Use interface methods or typeof narrowing 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 right bool, but the typechecker does not narrow obj.field. Assign to a local first:

    var inner = obj.field
    if (inner instanceof Foo) { /* inner narrowed */ }
  • VM: walks the class’s .Super chain comparing names.
  • AOT: uses reflection to read the concrete struct name, then walks a compile-time emitted __chuks_class_parents map.

Both backends produce identical observable behaviour.