Ruft zur Laufzeit den Klassennamen eines Objekts ab


271

Ist es möglich, den Klassen- / Typnamen eines Objekts zur Laufzeit mit TypeScript abzurufen?

class MyClass{}

var instance = new MyClass();
console.log(instance.????); // Should output "MyClass"

3
Siehe hier . Zur Laufzeit führen Sie JavaScript aus.
Matt Burland

1
Wie erhält man den Konstruktornamen in der TypeScript-Datei? Sie können this.constructor.name nicht in einer TypeScript-Methode (in der .ts-Datei) ausführen.
Sheldon_cooper

Antworten:


458

Einfache Antwort:

class MyClass {}

const instance = new MyClass();

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

Beachten Sie jedoch, dass der Name bei Verwendung von minimiertem Code wahrscheinlich anders sein wird.


14
Leider ist MyClass.name eine ES6-Funktion, daher funktioniert sie in IE11 nicht.
Begie

9
Typoskript wird einen Fehler auslösen. sollte tunlet instance: any = this.constructor; console.log(instance.name);
Subash

7
@Subash ein kürzerer Weg, um Casting zu vermeiden, anyistconsole.log(instance.constructor['name']);
Nick Strupat

1
@Subash Sie können stattdessen auch eine Typdeklaration erstellen: interface Function { name: string; }- Dadurch wird die "native" Definition erweitert.
John Weisz

1
MyClass.namefunktioniert nicht gut, wenn Sie Ihren Code minimieren. Weil dadurch der Name der Klasse minimiert wird.
AngryHacker

28

Ich weiß, dass ich zu spät zur Party komme, aber ich finde, dass das auch funktioniert.

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

Alternative...

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

Der obige Code ruft den gesamten Konstruktorcode als Zeichenfolge ab und wendet einen regulären Ausdruck an, um alle 'Wörter' zu erhalten. Das erste Wort sollte "Funktion" sein und das zweite Wort sollte der Name der Klasse sein.

Hoffe das hilft.


4
Entschuldigung, sicher. Normalerweise verwenden Sie Minifizierung, Hässlichkeit und andere Nachbearbeitungssysteme. Auf dem Produktionsserver ist Ihr Klassenname also nicht identisch. Und Ihr Code wird nicht funktionieren. Ich habe keine wirklich gute Lösung gefunden, um den Klassennamen zu erhalten. Am besten definieren Sie eine statische Variable mit Ihrem Klassennamen.
Dima Kurilo

23

Meine Lösung bestand darin, mich nicht auf den Klassennamen zu verlassen. object.constructor.name funktioniert theoretisch. Wenn Sie jedoch TypeScript in etwas wie Ionic verwenden, geht es in Flammen auf, sobald Sie zur Produktion gehen, da der Produktionsmodus von Ionic den Javascript-Code minimiert. So erhalten die Klassen benannte Dinge wie "a" und "e".

Am Ende hatte ich in allen meinen Objekten eine typeName-Klasse, der der Konstruktor den Klassennamen zuweist. So:

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

constructor() {
typeName = "Person";
}

Ja, das wurde wirklich nicht gefragt. Die Verwendung des Konstruktornamens für etwas, das möglicherweise später minimiert wird, bittet jedoch nur um Kopfschmerzen.


19

Siehe diese Frage .

Da TypeScript zu JavaScript kompiliert wird, führen Sie zur Laufzeit JavaScript aus, sodass dieselben Regeln gelten.


14

Sie müssen die Instanz zuerst umwandeln, anyda Functiondie Typdefinition keine nameEigenschaft hat.

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"

Aktualisieren:

Mit TypeScript 2.4 (und möglicherweise früher) kann der Code noch sauberer sein:

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"

2
Versucht auf TypeScript 2.6.2 und ich erhalte diesen Fehler:Property 'name' does not exist on type 'Function'.
orad

(this as {}).constructor.nameoder (this as object).constructor.nameist besser als anyweil du dann tatsächlich Autocomplete bekommst :-)
Simon_Weaver

4

Der vollständige TypeScript-Code

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

4
Möglicherweise treten Probleme auf, wenn Sie Ihren Typoskript- / Javascript-Code minimieren und optimieren. Es könnte Funktionsnamen ändern und dann könnte Ihr Klassennamenvergleich falsch sein.
Antti

4

In Angular2 kann dies helfen, den Komponentennamen zu erhalten:

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

comp: any wird benötigt, da beim Kompilieren von TypeScript Fehler auftreten, da Function zunächst keinen Eigenschaftsnamen hat.


5
Dies wird jedoch nicht funktionieren, wenn Sie Ihren Code minimieren / hässlich machen
Admir Sabanovic

Um einen verwendbaren 'Namen' einer Komponente zu erhalten, ist es besser, den Tag-Namen von element.nativeElement- In einer Direktive können Sie den Namen der Komponente wie @Optional() element: ElementRef<HTMLElement>if (element != null && element.nativeElement.tagName.startsWith('APP-')) { this.name = element.nativeElement.tagName; }
folgt abrufen

(und Tag-Namen werden nicht minimiert)
Simon_Weaver

4
  • Musste ". Prototype. " Hinzufügen, um zu verwenden : myClass.prototype.constructor.name.
  • Ansonsten mit folgendem Code : myClass.constructor.name, hatte ich den TypeScript-Fehler:

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


0

Diese Lösung funktioniert nach der Hässlichkeit der Minifizierung, erfordert jedoch das Dekorieren der Klassen mit Metadaten.

Wir verwenden die Codegenerierung, um unsere Entitätsklassen mit folgenden Metadaten zu dekorieren:

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

Dann konsumieren Sie mit folgendem Helfer:

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);
}

-2

Wenn Sie bereits wissen, welche Typen zu erwarten sind (z. B. wenn eine Methode einen Vereinigungstyp zurückgibt ), können Sie Typschutz verwenden.

Für primitive Typen können Sie beispielsweise einen Schutztyp verwenden :

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

Für komplexe Typen können Sie eine Instanz von guard verwenden :

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

Ich denke, es liegt daran, dass Ihre Antwort nicht mit der Frage zusammenhängt. Die Frage war, den Klassennamen dazu zu bringen, nicht bedingt Dinge über den Instanztyp zu tun.
Daniel Leiszen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.