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:
instanceis an objectinstance.constructorrefers to the function that created itconstructor.namegives 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 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:
interfaceandtype= design-time onlyclass= real runtime valueconstructor.name= read the class's runtime nameplate
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( ()));
.(( ()));
Step by Step Execution
Consider this code:
class MyClass {}
const instance = new MyClass();
const className = instance.constructor.name;
console.log(className);
Step by step:
class MyClass {}defines a class.new MyClass()creates a new object instance.- That instance internally keeps a link to the constructor used to create it.
instance.constructorrefers to theMyClassconstructor function.instance.constructor.namereads the constructor's name, which is"MyClass".console.log(className)prints:
MyClass
Trace example
class Animal {}
const pet = new ();
.(pet);
.(pet.);
.(pet..);
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
- Show clearer diagnostics such as
- 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 {}
{}
(): {
(obj.. !== ) {
();
}
}
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:
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:
typeandinterfaceare erased during compilationclassexists 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 !== && value !== ) {
.(value..);
}
Comparisons
| Approach | Example | Best for | Limitations |
|---|---|---|---|
constructor.name | instance.constructor.name | Debugging, logs, simple inspection | Can be affected by minification or renaming |
instanceof | instance instanceof MyClass | Checking if an object is from a class | Requires access to the class itself |
| Custom property | instance.type | Stable identifiers in app logic | Must be added manually |
typeof | typeof value | Primitive values like string, number, boolean |
Cheat Sheet
class MyClass {}
const instance = new MyClass();
console.log(instance.constructor.name); // "MyClass"
Quick rules
classexists at runtimetypeandinterfacedo not exist at runtimeconstructor.namegives the runtime class/function nameinstanceofis better for type checks than comparing name strings- Check for
nullandundefinedbefore accessing.constructor
Safe helper
function getRuntimeName(value: unknown): string {
if (value === null) return "null";
(value === ) ;
( value === || value === ) {
(value { ?: { ?: } }).?. ?? ;
}
value;
}
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".
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
unknownvalue - Return
nullandundefinedclearly when those values are passed - Use
typeoffor primitives - Use
constructor.namefor objects and class instances
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 @ViewChild Returning Undefined: Lifecycle, Child Components, and Fixes
Learn why Angular @ViewChild can be undefined, when it becomes available, and how to access child components correctly using lifecycle hooks.