Question
Runtime Type Checks in TypeScript: Classes, Interfaces, instanceof, and Type Guards
Question
In ActionScript, you can check a value's type at runtime with the is operator:
var mySprite:Sprite = new Sprite();
trace(mySprite is Sprite); // true
trace(mySprite is DisplayObject); // true
trace(mySprite is IEventDispatcher); // true
Is there an equivalent way in TypeScript to detect whether a variable is a certain class, extends a class, or implements a specific interface?
I could not find a clear answer in the language specification, but this seems important when working with classes and interfaces.
Short Answer
By the end of this page, you will understand how TypeScript handles runtime type checks, when to use instanceof, why interfaces cannot be checked directly at runtime, and how to write custom type guards for safe and practical type detection.
Concept
TypeScript has two worlds:
- Compile time: where TypeScript checks types for correctness
- Runtime: where JavaScript actually runs
This distinction is the key to understanding the question.
Classes exist at runtime
A TypeScript class becomes a JavaScript constructor function or class in the emitted code. That means you can check whether an object was created from a class using runtime tools like instanceof.
class Animal {}
class Dog extends Animal {}
const d = new Dog();
console.log(d instanceof Dog); // true
console.log(d instanceof Animal); // true
Interfaces do not exist at runtime
Interfaces are only used by TypeScript during compilation. They are removed when the code becomes JavaScript. Because of that, you cannot do this:
interface Flyable {
fly(): ;
}
Mental Model
Think of TypeScript types like labels on a blueprint and JavaScript runtime values like the actual building.
- A class is like a real building with a physical structure you can inspect later.
- An interface is like a design note on the blueprint. It helps during planning, but it is not physically attached to the building.
So at runtime:
- You can inspect a class-based object with
instanceof - You cannot inspect an interface directly, because it was never part of the final JavaScript
Another way to think about it:
instanceofasks: "Was this object built by this constructor or one of its parents?"- A type guard asks: "Does this object have the properties and methods I need?"
Syntax and Examples
Checking a class with instanceof
class DisplayObject {}
class Sprite extends DisplayObject {}
const mySprite = new Sprite();
console.log(mySprite instanceof Sprite); // true
console.log(mySprite instanceof DisplayObject); // true
Use instanceof when you are working with classes and want to know whether an object was created from a class or one of its parent classes.
Checking primitives with typeof
const value = "hello";
console.log(typeof value === "string"); // true
typeof is useful for primitive values such as:
Step by Step Execution
Consider this example:
class Animal {}
class Dog extends Animal {
bark() {
console.log("Woof");
}
}
function handlePet(value: unknown) {
if (value instanceof Dog) {
value.bark();
} else {
console.log("Not a Dog");
}
}
const pet = new Dog();
handlePet(pet);
Step by step
Animalis defined as a base class.DogextendsAnimal, so everyDogis also anAnimal.handlePetaccepts , which means TypeScript requires us to narrow the type before using it.
Real World Use Cases
Validating API responses
When data comes from an API, TypeScript types are not automatically enforced at runtime.
interface Product {
id: number;
name: string;
}
function isProduct(value: unknown): value is Product {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value
);
}
This helps prevent runtime errors when external data is incomplete or malformed.
Handling different class instances
In UI frameworks or game code, you may receive different object instances and need to behave differently depending on the class.
class Button {}
class TextField {}
function renderControl(control: Button | TextField) {
if (control instanceof ) {
.();
} {
.();
}
}
Real Codebase Usage
In real TypeScript codebases, developers usually combine several narrowing patterns.
Guard clauses
A common pattern is to exit early if a value is invalid.
function printUserName(user: unknown) {
if (typeof user !== "object" || user === null || !("name" in user)) {
return;
}
console.log(user.name);
}
This keeps the main logic clean.
Early return after instanceof
class ApiError extends Error {
code: number;
constructor(message: string, code: number) {
super(message);
this.code = code;
}
}
function handleError() {
(!(err )) {
.();
;
}
.(err., err.);
}
Common Mistakes
Mistake 1: Trying to use instanceof with an interface
interface Person {
name: string;
}
// if (value instanceof Person) {} // ❌
Why it fails
Interfaces do not exist at runtime.
Fix
Use a custom type guard:
function isPerson(value: unknown): value is Person {
return typeof value === "object" && value !== null && "name" in value;
}
Mistake 2: Forgetting null when checking objects
function isPerson(value: unknown): boolean {
return typeof value === "object" && "name" in value;
}
Comparisons
Runtime type checking options in TypeScript
| Tool | Works for | Runtime? | Example | Notes |
|---|---|---|---|---|
instanceof | Classes | Yes | value instanceof User | Checks prototype chain |
typeof | Primitives | Yes | typeof value === "string" | Limited to primitive categories |
in | Object properties | Yes | "name" in value | Good for shape checks |
| Custom type guard |
Cheat Sheet
Quick reference
Check a class
if (value instanceof MyClass) {
// value is MyClass here
}
Check a primitive
if (typeof value === "string") {
// value is string
}
Check whether a property exists
if (typeof value === "object" && value !== null && "name" in value) {
// value has a name property
}
Custom type guard for an interface
interface User {
id: number;
name: string;
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== &&
value &&
value
);
}
FAQ
Can TypeScript check interfaces at runtime?
No. Interfaces are removed during compilation, so they do not exist in JavaScript runtime code.
What is the TypeScript equivalent of ActionScript is?
For classes, the closest equivalent is instanceof. For interfaces, use a custom type guard.
Does instanceof work with inherited classes?
Yes. If Child extends Parent, then an instance of Child is also instanceof Parent.
Can I use typeof to check objects?
Only in a limited way. typeof can tell you whether something is an object, but not whether it matches a specific interface.
Is as MyType a runtime check?
No. It only tells the TypeScript compiler to treat a value as a certain type.
What should I use for API response validation?
Use runtime validation with property checks, custom type guards, or a validation library if the data shape is complex.
Why does TypeScript remove interfaces?
Because interfaces are a compile-time feature for static checking. JavaScript itself has no interface construct.
Mini Project
Description
Build a small runtime checker for a messaging system. Some messages are class instances, while others are plain objects from external data. This project demonstrates when to use instanceof and when to use a custom type guard.
Goal
Create a function that safely handles both class-based messages and interface-shaped messages.
Requirements
- Create a class called
SystemMessagewith atextproperty. - Create an interface called
UserMessagewithuserandtextproperties. - Write a custom type guard that checks whether an unknown value is a
UserMessage. - Write a
printMessagefunction that usesinstanceofforSystemMessageand the custom type guard forUserMessage. - Print a fallback message for invalid input.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular Dependency Injection: Fix "Can't Resolve All Parameters for Component" Errors
Learn why Angular shows "Can't resolve all parameters for component" and how to fix service injection issues in components.