instanceof narrowing
Narrows a value to a class type by testing its prototype chain at runtime.
value instanceof Constructor- Added in TypeScript 1.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$value | unknown | The expression being tested. Primitives always yield false, including a string tested against String. |
$Constructor | Function | Any value with a prototype property, normally a class or constructor function. Narrows to its instance type, or to the type declared by a `[Symbol.hasInstance]` predicate if one exists. |
Return value
booleanA boolean. True when Constructor.prototype appears anywhere in value's prototype chain; the true branch narrows value to the constructor's instance type.
Errors
TypeError at runtime if the right operand is not callable. tsc errors if the right operand is not of type `any` or assignable to `Function`.
Changelog
- PHP 1.4instanceof type guards introduced alongside typeof.
- PHP 4.4An instanceof check stored in a const is tracked by control flow analysis.
- PHP 5.3A `[Symbol.hasInstance]` method declared with a type predicate return type is honoured by instanceof narrowing.
instanceof narrows by walking the prototype chain, so it is the right tool for anything created with new: your own classes, built-ins like Date, Map and RegExp, and error hierarchies.
When to reach for it
- A
catchblock, where the caught value isunknownand you want to separate yourHttpErrorfrom a strayTypeErrorfrom a thrown string. - Distinguishing a
Datefrom an ISO string in a parameter that accepts both. - Any union whose members are classes rather than object literal types.
What trips people up
It does not work on plain object shapes. A type Fish = { swim(): void } has no constructor, so there is nothing to test against — reach for a type predicate or the in operator instead. This is the single most common reason people ask why instanceof "does not work with interfaces".
It also fails across realms: an object from an iframe or a worker has a different Array, so x instanceof Array is false while Array.isArray(x) is true.
Subclassing Error when targeting ES5 breaks the chain unless you call Object.setPrototypeOf(this, new.target.prototype) in the constructor.
Example
When to use it
- Separating your own error class from unexpected throws inside catch (e: unknown)
- Accepting a Date object or an ISO string and normalising both to a timestamp
- Walking a union of AST node classes without adding a discriminant field
Frequently asked questions
Why doesn't instanceof work with an interface?
Interfaces and type aliases are erased at compile time, so there is no constructor to test against at runtime. Use a type predicate function or the `in` operator to narrow plain object shapes.
Why is x instanceof Array false for an array?
Almost always a realm boundary — an array created in an iframe, worker, or another Node vm context has a different Array constructor. Use Array.isArray, which checks the internal slot instead of the prototype chain.
How do I narrow the error in a catch block?
The caught value is typed unknown under useUnknownInCatchVariables, so test it: `if (e instanceof Error)` gives you `.message`, and a check against your own subclass first gives you its extra fields.
More examples
Classes narrow with instanceof; plain shapes need a predicate
Prints 3.1416, 9, then true false. The Circle/Square union narrows with instanceof because both are classes; Point cannot, because `type Point` is erased at compile time and leaves nothing to test against at runtime.
class Circle { constructor(public radius: number) {} }
class Square { constructor(public side: number) {} }
// A plain object shape — no constructor, so instanceof is impossible.
type Point = { x: number; y: number };
function isPoint(v: object): v is Point {
return "x" in v && "y" in v;
}
function area(s: Circle | Square): number {
return s instanceof Circle ? Math.PI * s.radius ** 2 : s.side ** 2;
}
console.log(area(new Circle(1)).toFixed(4));
console.log(area(new Square(3)));
console.log(isPoint({ x: 1, y: 2 }), isPoint(new Circle(1)));Ordering matters with subclasses
Prints field(email), validation, generic. Because instanceof walks the whole prototype chain, a FieldError passes the ValidationError test too — putting the base class first would swallow every subclass and you would never reach `e.field`.
class ValidationError extends Error {}
class FieldError extends ValidationError {
constructor(public field: string) { super(`bad field: ${field}`); }
}
function classify(e: Error): string {
// Most specific first: a FieldError is also a ValidationError.
if (e instanceof FieldError) return `field(${e.field})`;
if (e instanceof ValidationError) return "validation";
return "generic";
}
console.log(classify(new FieldError("email")));
console.log(classify(new ValidationError("nope")));
console.log(classify(new Error("boom")));
Discussion