Question

Runtime Interface Type Checks in TypeScript: Why `instanceof` Does Not Work

typescriptjavascriptinterface

Question

In TypeScript, how can I determine at runtime whether a variable of type any matches an interface?

For example:

interface A {
  member: string;
}

let a: any = { member: "foobar" };

if (a instanceof A) {
  alert(a.member);
}

In the TypeScript Playground, the last line produces an error saying that A does not exist in the current scope. That seems confusing, because A is clearly declared and can be used in a type annotation like this:

let a: A = { member: "foobar" };

Looking at the generated JavaScript makes the issue clearer:

var a = {
  member: "foobar"
};

if (a instanceof A) {
  alert(a.member);
}

There is no runtime JavaScript value called A for the interface, so the runtime check cannot work.

I understand that JavaScript does not have interfaces at runtime. Is there any way to check whether an object conforms to an interface shape at runtime in TypeScript? Also, TypeScript seems to have an implements keyword or method in autocomplete—how is that actually used?

Short Answer

By the end of this page, you will understand why TypeScript interfaces disappear at runtime, why instanceof only works with classes and constructor functions, and how to perform safe runtime checks using type guards, property checks, and validation functions.

Concept

TypeScript has two worlds:

  • Type world: used by the compiler for checking your code
  • Runtime world: actual JavaScript values that exist when the program runs

An interface belongs only to the type world. It helps TypeScript verify that an object has the required shape during development, but it does not generate JavaScript code.

That is why this does not work:

interface A {
  member: string;
}

const a: any = { member: "foobar" };

// Error: A is not a runtime value
if (a instanceof A) {
  console.log(a.member);
}

instanceof is a runtime operator. It checks whether an object was created by a specific constructor function or class.

For example, this works because User exists at runtime:

class User {
  constructor(public member: string) {}
}

const u = new User("foobar");

console.log(u instanceof User); // true

But an interface like A is erased during compilation, so there is nothing for instanceof to compare against.

So how do you check an interface at runtime?

You check the shape of the object manually.

This is usually done with a type guard:

interface A {
  member: string;
}

function isA(value: any): value is A {
  return (
    value !== null &&
    typeof value === "object" &&
    typeof value.member === "string"
  );
}

Now TypeScript understands both the runtime check and the type narrowing:

const a: any = { member: "foobar" };

if (isA(a)) {
  console.log(a.member); // safe
}

What about implements?

implements is used with classes, not as a runtime check.

interface A {
  member: string;
}

class MyClass implements A {
  constructor(public member: string) {}
}

This tells TypeScript to verify that MyClass matches the interface. It does not add a runtime interface check.

So the key rule is:

  • Use instanceof for classes
  • Use type guards or validation functions for interfaces

Mental Model

Think of a TypeScript interface like a blueprint drawn on paper.

  • While designing the program, the blueprint is useful.
  • Once the building is finished, the blueprint is not part of the building.

At runtime, JavaScript only sees the finished building—the actual objects.

So if you ask JavaScript, "Was this object built from interface A?", it cannot answer, because the interface blueprint is gone.

A class is different. A class is more like a factory machine that still exists at runtime. That is why JavaScript can ask:

  • "Was this object created by this factory?" → instanceof

For interfaces, you instead ask:

  • "Does this object have the properties I expect?"
Take Quiz

Syntax and Examples

Core idea

Use a type guard function to check whether a value matches the structure of an interface.

Interface

interface A {
  member: string;
}

Type guard

function isA(value: any): value is A {
  return (
    value !== null &&
    typeof value === "object" &&
    typeof value.member === "string"
  );
}

Usage

const a: any = { member: "foobar" };

if (isA(a)) {
  console.log(a.member); // TypeScript now knows a is A
} else {
  console.log("Not an A");
}

Why instanceof works for classes only

class AClass {
  constructor(public member: string) {}
}

const a = new AClass("foobar");

if (a instanceof AClass) {
  console.log(a.member);
}

This works because AClass exists in generated JavaScript.

implements syntax

interface A {
  member: string;
}

class MyClass implements A {
  constructor(public member: string) {}
}

This means:

  • the class must provide all properties required by A
  • TypeScript checks this at compile time
  • no runtime interface check is created

Slightly safer version using unknown

Using unknown is usually better than any when validating input.

interface A {
  member: string;
}

function isA(value: unknown): value is A {
  return (
    typeof value === "object" &&
    value !== null &&
    "member" in value &&
    typeof (value as { member: unknown }).member === "string"
  );
}

This is a common real-world pattern when handling API data or parsed JSON.

Step by Step Execution

Consider this code:

interface A {
  member: string;
}

function isA(value: unknown): value is A {
  return (
    typeof value === "object" &&
    value !== null &&
    "member" in value &&
    typeof (value as { member: unknown }).member === "string"
  );
}

const input: unknown = { member: "hello" };

if (isA(input)) {
  console.log(input.member);
}

Step-by-step

  1. interface A defines the expected shape:

    • there must be a member
    • member must be a string
  2. input is declared as unknown.

    • TypeScript does not let you access properties directly on unknown
    • this forces you to validate first
  3. isA(input) runs.

  4. Inside isA:

    • typeof value === "object" checks that the value is an object-like value
    • value !== null avoids the JavaScript quirk where typeof null === "object"
    • "member" in value checks that the property exists
    • typeof ...member === "string" verifies the property type
  5. If all checks return true, then isA(input) returns true.

  6. TypeScript sees the special return type value is A and narrows input to A inside the if block.

  7. Now input.member is safe to access.

What if the data is wrong?

const input: unknown = { member: 123 };

if (isA(input)) {
  console.log(input.member);
} else {
  console.log("Invalid shape");
}

Here, the last check fails because 123 is not a string, so the code safely goes to the else branch.

Real World Use Cases

Runtime shape checks are common whenever data comes from outside your program.

API responses

interface UserDto {
  id: number;
  name: string;
}

When you fetch JSON from an API, TypeScript cannot guarantee the server sent the correct shape. A runtime guard can verify it before use.

Local storage or session storage

Data read from storage may be missing fields, have old formats, or contain corrupted values.

Form input processing

User input is unpredictable. You may need to confirm that an object contains the required fields before processing it.

Parsing JSON

const parsed: unknown = JSON.parse(text);

After parsing, runtime validation is often necessary before treating the data as a known interface.

Plugin systems or configuration files

Apps often load configuration from external files. Checking shape at runtime helps prevent crashes.

Message passing

In frontend apps, workers, browser events, or postMessage payloads may need runtime validation before use.

Take Quiz

Real Codebase Usage

In real projects, developers rarely check interfaces directly. Instead, they use a few common patterns.

1. Type guard functions

This is the most common pattern for lightweight validation.

function isA(value: unknown): value is A {
  return (
    typeof value === "object" &&
    value !== null &&
    "member" in value &&
    typeof (value as { member: unknown }).member === "string"
  );
}

2. Guard clauses

Validate early and return quickly.

function printMember(value: unknown) {
  if (!isA(value)) {
    throw new Error("Expected A");
  }

  console.log(value.member);
}

This keeps the rest of the function simple.

3. Validation at application boundaries

A common rule is:

  • validate data when it enters the system
  • use trusted types internally

For example:

  • API layer validates incoming JSON
  • service layer works with typed objects

4. Dedicated validators for complex objects

For larger objects, teams often write reusable validation helpers or use schema libraries. Even then, the idea is the same: validate structure at runtime because interfaces alone are not enough.

5. Prefer unknown over any

In real codebases, unknown is safer than any because it forces explicit checks before use.

function handle(value: unknown) {
  if (isA(value)) {
    console.log(value.member);
  }
}

With any, unsafe property access can slip through unnoticed.

Common Mistakes

1. Trying to use instanceof with an interface

Broken code:

interface A {
  member: string;
}

const a: any = { member: "x" };

if (a instanceof A) {
  console.log(a.member);
}

Why it fails:

  • A is not a runtime value
  • interfaces are removed during compilation

Fix:

function isA(value: unknown): value is A {
  return (
    typeof value === "object" &&
    value !== null &&
    "member" in value &&
    typeof (value as { member: unknown }).member === "string"
  );
}

2. Confusing implements with runtime checking

Broken idea:

class MyClass implements A {}

This does not mean you can later check obj against A at runtime.

implements only tells TypeScript to verify the class shape during compilation.

3. Checking only property existence, not property type

Broken code:

function isA(value: any): value is A {
  return value && "member" in value;
}

Problem:

const x = { member: 123 };

This would incorrectly pass.

Better:

function isA(value: unknown): value is A {
  return (
    typeof value === "object" &&
    value !== null &&
    "member" in value &&
    typeof (value as { member: unknown }).member === "string"
  );
}

4. Forgetting the null check

Broken code:

typeof value === "object"

Problem:

In JavaScript, null is also reported as an object.

Fix:

typeof value === "object" && value !== null

5. Using any everywhere

any disables many safety checks.

Prefer:

const data: unknown = JSON.parse(text);

Then validate before use.

6. Assuming a type assertion validates data

Broken idea:

const a = value as A;

This does not check anything at runtime. It only tells TypeScript to trust you.

Use a guard if the value is uncertain.

Comparisons

ConceptExists at runtime?Used forExample
interfaceNoCompile-time shape checkinginterface A { member: string }
classYesCreating objects and runtime checksclass AClass { ... }
instanceofYesChecking class/constructor identityobj instanceof AClass
implementsNo runtime effectEnsuring a class matches an interfaceclass X implements A
type guardYesChecking object shape at runtimefunction isA(v): v is A
type assertion (as)NoTelling TypeScript to trust a typevalue as A

instanceof vs type guard

FeatureinstanceofType guard
Works with interfacesNoYes
Works at runtimeYesYes
Checks constructor/class identityYesNo
Checks object shapeNoYes
Good for plain API objectsUsually noYes

any vs unknown

TypeSafer?Why
anyNoSkips type safety
unknownYesForces validation before use
Take Quiz

Cheat Sheet

Key rules

  • Interfaces are compile-time only in TypeScript.
  • Interfaces do not exist in generated JavaScript.
  • instanceof works with classes, not interfaces.
  • implements is used by classes to satisfy an interface at compile time.
  • For runtime interface-like checks, use a type guard.

Basic pattern

interface A {
  member: string;
}

function isA(value: unknown): value is A {
  return (
    typeof value === "object" &&
    value !== null &&
    "member" in value &&
    typeof (value as { member: unknown }).member === "string"
  );
}

Use it like this

const value: unknown = { member: "hello" };

if (isA(value)) {
  console.log(value.member);
}

Class example

class AClass {
  constructor(public member: string) {}
}

const a = new AClass("hello");
console.log(a instanceof AClass); // true

implements

interface A {
  member: string;
}

class MyClass implements A {
  constructor(public member: string) {}
}

Common pitfalls

  • instanceof A where A is an interface → invalid
  • value as A does not validate anything
  • typeof value === "object" must also check value !== null
  • Checking only property existence is often not enough; check property types too

FAQ

Why does TypeScript say an interface does not exist in the current scope?

Because instanceof expects a runtime value, such as a class or constructor function. An interface is only a compile-time type, so it is not available as a JavaScript value.

Can I use instanceof with an interface in TypeScript?

No. instanceof only works with classes, constructor functions, and objects that exist at runtime.

How do I check whether an object matches an interface?

Write a type guard function that checks the required properties and their types.

Does implements perform runtime checking?

No. implements only tells the TypeScript compiler to verify that a class matches an interface.

Is value as MyInterface a runtime check?

No. A type assertion only changes what TypeScript assumes. It does not inspect the actual data.

Should I use any or unknown for incoming data?

Prefer unknown. It forces you to validate the value before using it.

What should I use for API response validation?

For simple cases, write a type guard. For larger or nested data, use a dedicated validation approach or schema library.

Take Quiz

Mini Project

Description

Build a small runtime validator for user profile data. This demonstrates the exact situation where interfaces are useful for compile-time typing, but runtime checks are still necessary when data comes from an untrusted source like JSON or an API response.

Goal

Create a function that safely checks whether unknown input matches a UserProfile interface before using it.

Requirements

  • Define a UserProfile interface with id, name, and email fields.
  • Write a type guard function that validates the object at runtime.
  • Test the guard with one valid object and one invalid object.
  • Print the user name only when the value passes validation.
Take Quiz

Keep learning

Related questions

View more