Obtener el nombre de la clase de un objeto en tiempo de ejecución

272

¿Es posible obtener el nombre de clase / tipo de un objeto en tiempo de ejecución usando TypeScript?

class MyClass{}

var instance = new MyClass();
console.log(instance.????); // Should output "MyClass"
Adam Mills
fuente
3
Ver aquí . En tiempo de ejecución estás ejecutando JavaScript.
Matt Burland el
1
¿Cómo se obtiene el nombre del constructor en el archivo TypeScript? No puede hacer esto.constructor.name en un método TypeScript (en el archivo .ts).
sheldon_cooper

Respuestas:

464

Respuesta simple:

class MyClass {}

const instance = new MyClass();

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

Sin embargo: tenga en cuenta que es probable que el nombre sea diferente al usar código minificado.

Mikael Couzic
fuente
14
Desafortunadamente, MyClass.name es una característica de ES6, por lo tanto, no funciona en IE11.
comenzó
9
mecanografiado arrojará un error en esto. debería hacerlet instance: any = this.constructor; console.log(instance.name);
Subash
77
@Subash una manera más concisa para evitar la proyección de anyesconsole.log(instance.constructor['name']);
Nick Strupat
1
@Subash También podría crear una declaración de tipo en su lugar: interface Function { name: string; }esto ampliará la definición "nativa".
John Weisz
1
MyClass.nameno funcionará bien si está minimizando su código. Porque minimizará el nombre de la clase.
AngryHacker
28

Sé que llego tarde a la fiesta, pero creo que esto también funciona.

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

Alternativamente...

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

El código anterior obtiene todo el código del constructor como una cadena y aplica una expresión regular para obtener todas las 'palabras'. La primera palabra debe ser 'función' y la segunda palabra debe ser el nombre de la clase.

Espero que esto ayude.


fuente
44
Lo siento, claro. Por lo general, utiliza minificación, fecalificación y otros sistemas de procesamiento posterior. Entonces, en el servidor de producción, su nombre de clase no será el mismo. Y tu código no funcionará. No encontré una solución realmente buena para obtener el nombre de la clase. La forma más adecuada es definir una variable estática con su nombre de clase.
Dima Kurilo
23

Mi solución no fue confiar en el nombre de la clase. object.constructor.name funciona en teoría. Pero si está usando TypeScript en algo como Ionic, tan pronto como vaya a producción, se incendiará porque el modo de producción de Ionic minimiza el código Javascript. Entonces las clases reciben nombres como "a" y "e".

Lo que terminé haciendo fue tener una clase typeName en todos mis objetos a los que el constructor asigna el nombre de la clase. Entonces:

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

constructor() {
typeName = "Person";
}

Sí, eso no fue lo que se preguntó, realmente. Pero usar el constructor.name en algo que podría minimizarse en el futuro es solo un dolor de cabeza.

SonOfALink
fuente
19

Ver esta cuestión .

Dado que TypeScript está compilado en JavaScript, en tiempo de ejecución está ejecutando JavaScript, por lo que se aplicarán las mismas reglas.

Matt Burland
fuente
14

Primero debe convertir la instancia en anyporque Functionla definición de tipo no tiene una namepropiedad.

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"

Actualizar:

Con TypeScript 2.4 (y potencialmente anterior) el código puede ser aún más limpio:

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
fuente
2
Intenté con TypeScript 2.6.2 y Property 'name' does not exist on type 'Function'.
obtengo
(this as {}).constructor.nameo (this as object).constructor.namees mejor que anyporque entonces realmente obtienes autocompletar :-)
Simon_Weaver
5

En Angular2 esto puede ayudar a obtener el nombre de los componentes:

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

comp: se necesita cualquiera porque la compilación de TypeScript emitirá errores, ya que la función inicialmente no tiene un nombre de propiedad.

Admir Sabanovic
fuente
55
sin embargo, esto no funcionará si minimiza / uglifica su código
Admir Sabanovic
para obtener un 'nombre' utilizable de un componente, es mejor que obtenga el tagName de element.nativeElement: en una directiva, puede obtener el nombre del componente como este @Optional() element: ElementRef<HTMLElement>y luego usar if (element != null && element.nativeElement.tagName.startsWith('APP-')) { this.name = element.nativeElement.tagName; }
Simon_Weaver
(y los nombres de las etiquetas no se minimizan)
Simon_Weaver
4

El código completo de TypeScript

public getClassName() {
    var funcNameRegex = /function (.{1,})\(/;
    var results  = (funcNameRegex).exec(this["constructor"].toString());
    return (results && results.length > 1) ? results[1] : "";
}
Nati Krisi
fuente
44
Puede tener algunos problemas si minimiza y optimiza su código mecanografiado / javascript. Puede cambiar los nombres de las funciones y luego la comparación del nombre de su clase puede ser incorrecta.
Antti
4
  • Tenido que añadir " prototipo. " Para su uso: myClass.prototype.constructor.name.
  • De lo contrario, con el siguiente código: myClass.constructor.nametuve el error TypeScript:

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

Flox
fuente
0

Esta solución funciona después de la uglificación de minificación, pero requiere decorar las clases con metadatos.

Utilizamos la generación de código para decorar nuestras clases de entidad con metadatos de la siguiente manera:

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

Luego consumir con el siguiente ayudante:

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);
}
ttugates
fuente
-2

Si ya sabe qué tipos esperar (por ejemplo, cuando un método devuelve un tipo de unión ), puede usar protectores de tipo.

Por ejemplo, para los tipos primitivos puede usar un tipo de protección :

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

Para tipos complejos puede usar una instancia de guardia :

if (thing instanceof Array) {
  // Do stuff
}
Cocowalla
fuente
Supongo que es porque tu respuesta no está relacionada con la pregunta. La pregunta era obtener el nombre de la clase para no hacer condicionalmente cosas en el tipo de instancia.
Daniel Leiszen