437

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"
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Adam Mills
  • 7,719
  • 3
  • 31
  • 47
  • 5
    See [here](http://stackoverflow.com/questions/332422/how-do-i-get-the-name-of-an-objects-type-in-javascript). At runtime you are running JavaScript. – Matt Burland Nov 28 '12 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). – kind_robot Dec 14 '12 at 15:14
  • And what about the name of an interface? – Rvanlaak Feb 08 '23 at 16:15

11 Answers11

718

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.

Arjan
  • 22,808
  • 11
  • 61
  • 71
Mikael Couzic
  • 12,283
  • 5
  • 22
  • 16
  • 15
    Unfortunately MyClass.name is an ES6 feature hence it does not work in IE11. – begie May 09 '16 at 15:30
  • 15
    typescript will throw error on this. should do `let instance: any = this.constructor; console.log(instance.name); ` – Subash Jul 29 '16 at 06:26
  • 12
    @Subash a terser way to avoid casting to `any` is `console.log(instance.constructor['name']);` – Nick Strupat Sep 02 '16 at 07:47
  • 1
    @Subash You could also create a type-declaration instead: `interface Function { name: string; }` -- this will extend the "native" definition. – John Weisz Sep 21 '16 at 12:52
  • How about getting the name of a function? Like the C#'s nameOf ? – gkb Dec 08 '16 at 06:40
  • Doesn't work in TypeScript: "Property 'name' does not exist on type 'Function'." Nick's version works. – skoll Dec 17 '16 at 16:37
  • Doesn't work out of the box. You need to cast to any first before you can use `constructor.name` same with @NickStrupat suggested solution – starcorn Dec 21 '16 at 13:29
  • I think it should be noted that the two options are close but not quite equivalent. In case of subclassing, `this.constructor.name` will give you the child class name while the other one will obviously stay fixed to the parent. – Cristian Vrabie Jan 29 '17 at 16:06
  • You can polyfill Function.name so that this strategy will work on more browsers: https://github.com/JamesMGreene/Function.name – Rick Velde Feb 13 '17 at 16:05
  • 15
    `MyClass.name` won't work well if you are minifying your code. Because it will minify the name of the class. – AngryHacker Aug 05 '19 at 18:20
  • Once minified, I found out that the constructor name just turns to "e". Any ways to solve this? – Henry Oct 08 '20 at 03:01
  • 3
    @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. – Mikael Couzic Oct 09 '20 at 08:54
  • @MikaelCouzic yeah, its okay, but thanks for the info :) – Henry Oct 09 '20 at 09:55
40

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.

SonOfALink
  • 471
  • 4
  • 10
  • 6
    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. – Christopher Chase Mar 12 '21 at 12:29
  • 1
    @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. – tooleks Jun 02 '21 at 16:53
  • 1
    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. – theaceofthespade Sep 27 '21 at 01:38
  • This solution will be easier to implement across frontend and backend, since minification will not effect it. – theaceofthespade Sep 27 '21 at 01:42
30

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.

  • 9
    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. – Dima Kurilo Oct 31 '16 at 13:49
26

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"
Westy92
  • 19,087
  • 4
  • 72
  • 54
  • 3
    Tried on TypeScript 2.6.2 and I'm getting this error: `Property 'name' does not exist on type 'Function'.` – orad Jan 11 '18 at 23:05
  • `(this as {}).constructor.name` or `(this as object).constructor.name` is better than `any` because then you actually get autocomplete :-) – Simon_Weaver Jan 16 '19 at 22:31
20

See this question.

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

Community
  • 1
  • 1
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
16

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
ttugates
  • 5,818
  • 3
  • 44
  • 54
  • 1
    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 (https://www.npmjs.com/package/reflect-metadata) Could you please integrate this info in you answer? – lbrutti Nov 18 '20 at 09:13
  • @lbrutti you must be import the reflect-metadata package first. Code import "reflect-metadata"; into top of your source file. then, use reflect. – Wooyoung Tyler Kim Nov 19 '20 at 09:35
  • 1
    @WooyoungTylerKim that's what i did ;) i was just asking to highlight this in the answer to make it even more useful. – lbrutti Nov 20 '20 at 15:57
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.

Yogesh Umesh Vaity
  • 41,009
  • 21
  • 145
  • 105
Admir Sabanovic
  • 645
  • 1
  • 11
  • 18
  • 9
    however this will not work if you minify/uglify your code – Admir Sabanovic Dec 22 '16 at 12:49
  • 1
    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` and then use `if (element != null && element.nativeElement.tagName.startsWith('APP-')) { this.name = element.nativeElement.tagName; } ` – Simon_Weaver Jan 16 '19 at 22:19
  • (and tag names don't get minified) – Simon_Weaver Jan 16 '19 at 22:21
6
  • 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'.

wonea
  • 4,783
  • 17
  • 86
  • 139
Flox
  • 145
  • 3
  • 10
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] : "";
}
wonea
  • 4,783
  • 17
  • 86
  • 139
Nati Krisi
  • 1,023
  • 1
  • 12
  • 23
  • 4
    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. – Antti Mar 18 '15 at 21:42
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

-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
}
Cocowalla
  • 13,822
  • 6
  • 66
  • 112