Question

Get an Object's Class Name at Runtime in TypeScript

Question

Is it possible to get an object's class or type name at runtime in TypeScript?

class MyClass {}

const instance = new MyClass();
console.log(instance.????); // Should output "MyClass"

How can you access the runtime class name of an object instance in TypeScript, and what should you be aware of when doing this?

Short Answer

By the end of this page, you will understand the difference between TypeScript types and JavaScript runtime values, how to read an object's class name using its constructor, and when this approach is useful or unreliable in real projects.

Concept

TypeScript adds a type system on top of JavaScript, but those types mostly disappear after compilation. That means TypeScript interfaces, type aliases, and many type annotations do not exist at runtime.

However, classes do exist at runtime, because they compile to JavaScript constructor functions. So if an object was created from a class, you can usually inspect its constructor and read its name:

class MyClass {}

const instance = new MyClass();
console.log(instance.constructor.name); // "MyClass"

This works because:

  • instance is an object
  • instance.constructor refers to the function that created it
  • constructor.name gives the function or class name

This matters because developers often want runtime type information for:

  • logging
  • debugging
  • error messages
  • serialization helpers
  • framework internals

But there is an important limitation: TypeScript's type system is not runtime reflection. If you write:

type User = {
  name: string;
};

There is no User type object available at runtime. Only real JavaScript values, such as classes and functions, can be inspected this way.

Also note that class names can become unreliable in some production builds if code is minified or transformed. So constructor.name is convenient, but should not always be treated as a stable identifier for business logic.

Mental Model

Think of TypeScript types as labels on a blueprint and JavaScript classes as actual machines in a factory.

  • The blueprint labels help during design time
  • The machines exist in the real factory at runtime

When your program runs, the blueprint-only labels are gone. But the machine still exists, and it often has a nameplate on it. constructor.name is like reading that nameplate.

So:

  • interface and type = design-time only
  • class = real runtime value
  • constructor.name = read the class's runtime nameplate
Take Quiz

Syntax and Examples

The most common syntax is:

instance.constructor.name

Basic example

class MyClass {}

const instance = new MyClass();
console.log(instance.constructor.name); // "MyClass"

Here, instance.constructor points to MyClass, and .name reads the class name.

Another example with a helper function

class User {}
class Product {}

function getClassName(value: object): string {
  return value.constructor.name;
}

console.log(getClassName(new User()));    // "User"
console.log(getClassName(new Product())); // "Product"

This is useful for logging and debugging.

Safer version for unknown values

If the value might be null or undefined, add checks:

function getClassNameSafe(value: unknown): string {
  if (value === null) return "null";
  if (value === undefined) return "undefined";

  if (typeof value === "object" || typeof value === "function") {
    return (value as { constructor?: { name?: string } }).constructor?.name ?? "Unknown";
  }

  return typeof value;
}

console.log(getClassNameSafe(new Date())); // "Date"
console.log(getClassNameSafe(null));       // "null"
console.log(getClassNameSafe(42));         // "number"

This version is more practical in real applications because it handles more than just class instances.

Step by Step Execution

Consider this code:

class MyClass {}

const instance = new MyClass();
const className = instance.constructor.name;
console.log(className);

Step by step:

  1. class MyClass {} defines a class.
  2. new MyClass() creates a new object instance.
  3. That instance internally keeps a link to the constructor used to create it.
  4. instance.constructor refers to the MyClass constructor function.
  5. instance.constructor.name reads the constructor's name, which is "MyClass".
  6. console.log(className) prints:
MyClass

Trace example

class Animal {}

const pet = new Animal();
console.log(pet);                  // Animal {}
console.log(pet.constructor);      // [class Animal] or function Animal(...)
console.log(pet.constructor.name); // "Animal"

The exact printed constructor format depends on the environment, but .name is the part that gives the readable class name.

Real World Use Cases

Reading a class name at runtime is most useful in support code, not core business logic.

Common uses

  • Debug logging
    • Log what kind of object reached a function
  • Error messages
    • Show clearer diagnostics such as Expected User but received Product
  • Framework internals
    • Dependency injection or metadata systems may inspect constructors
  • Testing tools
    • Print readable object information during test failures
  • Developer tools
    • Inspect instances in debugging panels

Example: logging

class OrderService {}

function logInstance(obj: object): void {
  console.log(`Created instance of ${obj.constructor.name}`);
}

logInstance(new OrderService());

Example: custom error message

class User {}
class Admin {}

function expectUser(obj: object): void {
  if (obj.constructor.name !== "User") {
    throw new Error(`Expected User, got ${obj.constructor.name}`);
  }
}

This can help during development, although checking names as strings is usually not the strongest production approach.

Take Quiz

Real Codebase Usage

In real codebases, developers usually use runtime class names for debugging and tooling, while using stronger checks for application logic.

Common patterns

1. Logging and tracing

function debugObject(obj: object): void {
  console.log(`[DEBUG] ${obj.constructor.name}`);
}

2. Guard clauses

function printEntity(entity: unknown): void {
  if (!entity || typeof entity !== "object") {
    console.log("Not an object");
    return;
  }

  console.log(`Entity type: ${entity.constructor.name}`);
}

Guard clauses prevent runtime errors when values are missing or invalid.

3. Validation with instanceof

Instead of comparing names, developers often do this:

class User {}

function processUser(value: unknown): void {
  if (!(value instanceof User)) {
    throw new Error("Value must be a User instance");
  }

  console.log("Processing user");
}

This is usually better than:

value.constructor.name === "User"

because it avoids fragile string comparisons.

4. Explicit identifiers

When names must remain stable across builds, developers often add their own property:

class MyClass {
  static readonly type = "MyClass";
  readonly type = "MyClass";
}

const instance = new MyClass();
console.log(instance.type); // "MyClass"

This is more reliable if minification or bundling changes constructor names.

5. Error handling helpers

function describeValue(value: unknown): string {
  if (value === null) return "null";
  if (value === undefined) return "undefined";
  if (typeof value === "object") return value.constructor.name;
  return typeof value;
}

This is a practical pattern in API clients, validation libraries, and debugging utilities.

Common Mistakes

1. Expecting TypeScript types to exist at runtime

This does not work:

type User = {
  name: string;
};

// There is no runtime User type object here

Avoid this by remembering:

  • type and interface are erased during compilation
  • class exists at runtime

2. Using constructor.name on null or undefined

Broken code:

const value = null;
console.log(value.constructor.name);

This throws an error because null has no constructor.

Use checks first:

if (value !== null && value !== undefined) {
  console.log(value.constructor.name);
}

3. Comparing class names as strings in important logic

Fragile approach:

if (obj.constructor.name === "User") {
  // ...
}

Better approach:

if (obj instanceof User) {
  // ...
}

String checks can break if code is minified or renamed.

4. Assuming all objects have useful constructor names

Example:

console.log(({}).constructor.name); // "Object"
console.log(([]).constructor.name); // "Array"

These names are valid, but they may be too general for your needs.

5. Forgetting that transpilation and minification can affect names

In some build setups, a class name may not stay exactly as written in source code. If you need a permanent identifier, define one yourself:

class User {
  readonly type = "user";
}

Comparisons

ApproachExampleBest forLimitations
constructor.nameinstance.constructor.nameDebugging, logs, simple inspectionCan be affected by minification or renaming
instanceofinstance instanceof MyClassChecking if an object is from a classRequires access to the class itself
Custom propertyinstance.typeStable identifiers in app logicMust be added manually
typeoftypeof valuePrimitive values like string, number, booleanNot useful for distinguishing custom classes

typeof vs constructor.name

console.log(typeof "hello"); // "string"
console.log(typeof 42);       // "number"
console.log(typeof new Date()); // "object"

typeof is good for primitives, but not for custom class names.

instanceof vs constructor.name

class User {}
const user = new User();

console.log(user.constructor.name); // "User"
console.log(user instanceof User);  // true

Use:

  • constructor.name when you want a readable name
  • instanceof when you want a reliable class check
Take Quiz

Cheat Sheet

class MyClass {}
const instance = new MyClass();

console.log(instance.constructor.name); // "MyClass"

Quick rules

  • class exists at runtime
  • type and interface do not exist at runtime
  • constructor.name gives the runtime class/function name
  • instanceof is better for type checks than comparing name strings
  • Check for null and undefined before accessing .constructor

Safe helper

function getRuntimeName(value: unknown): string {
  if (value === null) return "null";
  if (value === undefined) return "undefined";
  if (typeof value === "object" || typeof value === "function") {
    return (value as { constructor?: { name?: string } }).constructor?.name ?? "Unknown";
  }
  return typeof value;
}

Prefer these choices

  • For readable logging: value.constructor.name
  • For class checks: value instanceof MyClass
  • For stable app identifiers: add your own type field

Watch out for

  • null.constructor error
  • Minified class names
  • Confusing runtime classes with TypeScript-only types

FAQ

Can I get a TypeScript interface name at runtime?

No. Interfaces and type aliases are removed during compilation, so they do not exist at runtime.

What is the simplest way to get a class name in TypeScript?

Use instance.constructor.name.

console.log(instance.constructor.name);

Is constructor.name reliable in production?

It is often fine for debugging, but it may become unreliable if your build process renames or minifies classes.

Should I use constructor.name for type checking?

Usually no. Prefer instanceof for class-based checks.

Why does typeof return "object" instead of my class name?

typeof only returns broad JavaScript categories like "string", "number", "boolean", "object", and "function".

What happens if the value is null?

Accessing null.constructor throws an error. Always check for null first.

Is this TypeScript-specific?

Not really. This behavior comes from JavaScript runtime objects. TypeScript just adds static typing on top.

Take Quiz

Mini Project

Description

Build a small runtime inspector utility that reports what kind of value it receives. This demonstrates the difference between primitive type checks, class names, and safer handling of unknown values.

Goal

Create a function that prints a useful runtime description for primitives, class instances, null, and plain objects.

Requirements

  • Create at least two custom classes
  • Write a function that accepts an unknown value
  • Return null and undefined clearly when those values are passed
  • Use typeof for primitives
  • Use constructor.name for objects and class instances
Take Quiz

Keep learning

Related questions

View more