473

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

class MyClass{}

var instance = new MyClass();
console.log(instance.????); // Should output "MyClass"
3
  • 5
    See here. At runtime you are running JavaScript. Commented Nov 28, 2012 at 20:14
  • 4
    How do you get the constructor name in TypeScript file though? You can't do this.constructor.name in a TypeScript method (in .ts file). Commented Dec 14, 2012 at 15:14
  • And what about the name of an interface? Commented Feb 8, 2023 at 16:15

11 Answers 11

782

Simple answer :

class MyClass {}

const instance = new MyClass();

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

However: beware that the name will likely be different when using minified code.

Sign up to request clarification or add additional context in comments.

16 Comments

Unfortunately MyClass.name is an ES6 feature hence it does not work in IE11.
typescript will throw error on this. should do let instance: any = this.constructor; console.log(instance.name);
@Subash a terser way to avoid casting to any is console.log(instance.constructor['name']);
MyClass.name won't work well if you are minifying your code. Because it will minify the name of the class.
Unfortunately, checked, what a headache! any solutions? Any other patches in the web world?
Not really. You can always hardwire name of the the class.
@Henry not that I know of. Minification will really rename the class. Depending on what you are trying to achieve, using the constructor name at runtime might not be the best strategy.
|
45

My solution was not to rely on the class name. object.constructor.name works in theory. But if you're using TypeScript in something like Ionic, as soon as you go to production it's going to go up in flames because Ionic's production mode minifies the Javascript code. So the classes get named things like "a" and "e."

What I ended up doing was having a typeName class in all my objects that the constructor assigns the class name to. So:

export class Person {
id: number;
name: string;
typeName: string;

constructor() {
typeName = "Person";
}

Yes that wasn't what was asked, really. But using the constructor.name on something that might potentially get minified down the road is just begging for a headache.

4 Comments

event after the code is minified , i'm fairly sure you can still do ` let instance=new Person (); (instance.constructor.name==Person.name) both names should get minified to the same thing.
@ChristopherChase It's expected that both names will be minified to the same thing, but usually, it's gonna be something short and non-unique like a, b, c, etc. so you shouldn't rely on this.
I think this is probably the correct answer for most people looking this up. I think it's important to be mindful of where Typescript "exists," and be weary of relying on it at runtime. It is very easy/tempting (at least for me) to use the classnames directly in the data, but ask yourself: at that point, is the data representing the class, or is the class representing the data? I'm not telling you it always has to be one way or the other, but I am saying you have to pick one and stick with it, or you will be chasing your tail.
This solution will be easier to implement across frontend and backend, since minification will not effect it.
32

I know I'm late to the party, but I find that this works too.

var constructorString: string = this.constructor.toString();
var className: string = constructorString.match(/\w+/g)[1]; 

Alternatively...

var className: string = this.constructor.toString().match(/\w+/g)[1];

The above code gets the entire constructor code as a string and applies a regex to get all 'words'. The first word should be 'function' and the second word should be the name of the class.

Hope this helps.

1 Comment

Sorry, sure. Usually, you use minification, uglyfication and other post processing systems. So on production server your class name will not be the same. And your code will not work. I didn't find really good solution to get class name. The most suitable way is to define a static variable with your class name.
27

You need to first cast the instance to any because Function's type definition does not have a name property.

class MyClass {
  getName() {
    return (<any>this).constructor.name;
    // OR return (this as any).constructor.name;
  }
}

// From outside the class:
var className = (<any>new MyClass()).constructor.name;
// OR var className = (new MyClass() as any).constructor.name;
console.log(className); // Should output "MyClass"

// From inside the class:
var instance = new MyClass();
console.log(instance.getName()); // Should output "MyClass"

Update:

With TypeScript 2.4 (and potentially earlier) the code can be even cleaner:

class MyClass {
  getName() {
    return this.constructor.name;
  }
}

// From outside the class:
var className = (new MyClass).constructor.name;
console.log(className); // Should output "MyClass"

// From inside the class:
var instance = new MyClass();
console.log(instance.getName()); // Should output "MyClass"

3 Comments

Tried on TypeScript 2.6.2 and I'm getting this error: Property 'name' does not exist on type 'Function'.
(this as {}).constructor.name or (this as object).constructor.name is better than any because then you actually get autocomplete :-)
I think Function.name was added to ES2015, so it's not the typescript version only, you also need it configured for the correct ES target.
22

Solution using Decorators that survives minification/uglification

We use code generation to decorate our Entity classes with metadata like so:

@name('Customer')
export class Customer {
  public custId: string;
  public name: string;
}

Then consume with the following helper:

export const nameKey = Symbol('name');

/**
 * To perserve class name though mangling.
 * @example
 * @name('Customer')
 * class Customer {}
 * @param className
 */
export function name(className: string): ClassDecorator {
  return (Reflect as any).metadata(nameKey, className);
}

/**
 * @example
 * const type = Customer;
 * getName(type); // 'Customer'
 * @param type
 */
export function getName(type: Function): string {
  return (Reflect as any).getMetadata(nameKey, type);
}

/**
 * @example
 * const instance = new Customer();
 * getInstanceName(instance); // 'Customer'
 * @param instance
 */
export function getInstanceName(instance: Object): string {
  return (Reflect as any).getMetadata(nameKey, instance.constructor);
}

Extra info:

  • You may need to install reflect-metadata
  • reflect-metadata is pollyfill written by members ot TypeScript for the proposed ES7 Reflection API
  • The proposal for decorators in JS can be tracked here

3 Comments

Hello, thanks for this solution! But trying to use you decorator, I get "Reflect.metadata is not a function" error. To solve this "reflect-metadata" package has to be installed (npmjs.com/package/reflect-metadata) Could you please integrate this info in you answer?
@lbrutti you must be import the reflect-metadata package first. Code import "reflect-metadata"; into top of your source file. then, use reflect.
@WooyoungTylerKim that's what i did ;) i was just asking to highlight this in the answer to make it even more useful.
18

See this question.

Since TypeScript is compiled to JavaScript, at runtime you are running JavaScript, so the same rules will apply.

Comments

7
  • Had to add ".prototype." to use : myClass.prototype.constructor.name .
  • Otherwise with the following code : myClass.constructor.name, I had the TypeScript error :

error TS2339: Property 'name' does not exist on type 'Function'.

Comments

7

In Angular2, this can help to get components name:

    getName() {
        let comp:any = this.constructor;
        return comp.name;
    }

comp:any is needed because TypeScript compiler will issue errors since Function initially does not have property name.

3 Comments

however this will not work if you minify/uglify your code
to get a usable 'name' of a component you're better off getting the tagName of element.nativeElement - On a directive you can get the component's name like this @Optional() element: ElementRef<HTMLElement> and then use if (element != null && element.nativeElement.tagName.startsWith('APP-')) { this.name = element.nativeElement.tagName; }
(and tag names don't get minified)
3

The full TypeScript code

public getClassName() {
    var funcNameRegex = /function (.{1,})\(/;
    var results  = (funcNameRegex).exec(this["constructor"].toString());
    return (results && results.length > 1) ? results[1] : "";
}

1 Comment

You might get some problems if you minimize and optimize your typescript/javascript code. It might change function names and then your class name comparison might be wrong.
0

I don't know since when this is possible, but right now it is:

class BeeKeeper {
  hasMask: boolean;
}

class ZooKeeper {
  nametag: string;
}

class Animal {
  numLegs: number;
}

class Bee extends Animal {
  keeper: BeeKeeper;
}

class Lion extends Animal {
  keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
  console.log(c.name) //this will give the class name
  return new c();
}

createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;

Credits to this tutorial

Comments

-1

If you already know what types to expect (for example, when a method returns a union type), then you can use type guards.

For example, for primitive types you can use a typeof guard:

if (typeof thing === "number") {
  // Do stuff
}

For complex types you can use an instanceof guard:

if (thing instanceof Array) {
  // Do stuff
}

2 Comments

I guess its because your answer is not related to the question. The question was to get the class name not to conditionally Do stuff on instance type.
found it useful

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.