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"
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"
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.
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.
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.
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"
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"
See this question.
Since TypeScript is compiled to JavaScript, at runtime you are running JavaScript, so the same rules will apply.
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:
reflect-metadata
reflect-metadata
is pollyfill written by members ot TypeScript for the proposed ES7 Reflection APIIn 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.
The full TypeScript code
public getClassName() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec(this["constructor"].toString());
return (results && results.length > 1) ? results[1] : "";
}
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
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
}