Uzyskaj nazwę klasy obiektu w czasie wykonywania

271

Czy można uzyskać nazwę klasy / typu obiektu w czasie wykonywania przy użyciu TypeScript?

class MyClass{}

var instance = new MyClass();
console.log(instance.????); // Should output "MyClass"
Adam Mills
źródło
3
Zobacz tutaj . W czasie wykonywania korzystasz z JavaScript.
Matt Burland,
1
Jak uzyskać nazwę konstruktora w pliku TypeScript? Nie można zrobić this.constructor.name w metodzie TypeScript (w pliku .ts).
sheldon_cooper

Odpowiedzi:

461

Prosta odpowiedź:

class MyClass {}

const instance = new MyClass();

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

Należy jednak pamiętać, że nazwa będzie prawdopodobnie inna przy użyciu kodu zminimalizowanego.

Mikael Couzic
źródło
14
Niestety MyClass.name jest funkcją ES6, dlatego nie działa w IE11.
od
9
maszynopis rzuci na to błąd. powinien zrobićlet instance: any = this.constructor; console.log(instance.name);
Subash
7
@Subash szybszym sposobem na uniknięcie przesyłania anyjestconsole.log(instance.constructor['name']);
Nick Strupat
1
@Subash Zamiast tego możesz również utworzyć deklarację typu: interface Function { name: string; }- spowoduje to rozszerzenie definicji „natywnej”.
John Weisz
1
MyClass.namenie będzie działać dobrze, jeśli minimalizujesz swój kod. Ponieważ zminimalizuje nazwę klasy.
AngryHacker,
28

Wiem, że jestem spóźniony na imprezę, ale stwierdzam, że to też działa.

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

Alternatywnie...

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

Powyższy kod pobiera cały kod konstruktora jako ciąg znaków i stosuje wyrażenie regularne, aby uzyskać wszystkie „słowa”. Pierwszym słowem powinna być „funkcja”, a drugim słowem powinna być nazwa klasy.

Mam nadzieję że to pomoże.


źródło
4
Przepraszam Zwykle używasz systemów minimalizacji, brzydoty i innych systemów przetwarzania końcowego. Na serwerze produkcyjnym nazwa klasy nie będzie taka sama. Twój kod nie będzie działać. Nie znalazłem naprawdę dobrego rozwiązania, aby uzyskać nazwę klasy. Najbardziej odpowiednim sposobem jest zdefiniowanie zmiennej statycznej za pomocą nazwy klasy.
Dima Kurilo
23

Moim rozwiązaniem nie było poleganie na nazwie klasy. object.constructor.name działa w teorii. Ale jeśli używasz TypeScript w czymś takim, jak Ionic, jak tylko przejdziesz do produkcji, stanie się płomieniem, ponieważ tryb produkcji Ionic minimalizuje kod JavaScript. Tak więc klasy otrzymują nazwy takie jak „a” i „e”.

Skończyło się na tym, że we wszystkich moich obiektach posiadałem klasę typeName, do której konstruktor przypisuje nazwę klasy. Więc:

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

constructor() {
typeName = "Person";
}

Tak, naprawdę nie o to pytano. Ale użycie konstruktora.nazwa na czymś, co może potencjalnie zostać zminimalizowane na drodze, błaga o ból głowy.

SonOfALink
źródło
19

Zobacz to pytanie .

Ponieważ TypeScript jest kompilowany do JavaScript, w czasie wykonywania używasz JavaScript, więc obowiązują te same reguły.

Matt Burland
źródło
14

Najpierw musisz rzutować instancję, anyponieważ Functiondefinicja typu nie ma namewłaściwości.

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"

Aktualizacja:

W TypeScript 2.4 (i potencjalnie wcześniej) kod może być jeszcze czystszy:

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
źródło
2
Próbowałem na TypeScript 2.6.2 i otrzymuję ten błąd:Property 'name' does not exist on type 'Function'.
orad
(this as {}).constructor.namelub (this as object).constructor.namejest lepszy niż anydlatego, że faktycznie dostajesz autouzupełnianie :-)
Simon_Weaver
5

W Angular2 może to pomóc uzyskać nazwę komponentów:

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

comp: każdy jest potrzebny, ponieważ kompilacja TypeScript będzie powodować błędy, ponieważ funkcja początkowo nie ma nazwy właściwości.

Admir Sabanovic
źródło
5
jednak to nie zadziała, jeśli zminimalizujesz / uglifikujesz swój kod
Admir Sabanovic 22.12.16
aby uzyskać użyteczną „nazwę” komponentu, lepiej jest uzyskać tagName of element.nativeElement- zgodnie z dyrektywą możesz uzyskać nazwę komponentu w ten sposób, @Optional() element: ElementRef<HTMLElement>a następnie użyć if (element != null && element.nativeElement.tagName.startsWith('APP-')) { this.name = element.nativeElement.tagName; }
Simon_Weaver
(i nazwy znaczników nie są
pomniejszane
4

Pełny kod TypeScript

public getClassName() {
    var funcNameRegex = /function (.{1,})\(/;
    var results  = (funcNameRegex).exec(this["constructor"].toString());
    return (results && results.length > 1) ? results[1] : "";
}
Nati Krisi
źródło
4
Możesz napotkać pewne problemy, jeśli zminimalizujesz i zoptymalizujesz kod maszynowy / javascript. Może to zmienić nazwy funkcji, a następnie porównanie nazw klas może być nieprawidłowe.
Antti
4
  • Musiał dodać „ prototyp. ” Sposób użycia: myClass.prototype.constructor.name.
  • W przeciwnym razie z następującym kodem: myClass.constructor.namemiałem błąd TypeScript:

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

Flox
źródło
0

To rozwiązanie działa po zmglifikacji minimalizacji, ale wymaga udekorowania klas metadanymi.

Używamy generowania kodu, aby udekorować nasze klasy jednostek za pomocą takich metadanych:

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

Następnie skonsultuj się z następującym pomocnikiem:

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
źródło
-2

Jeśli już wiesz, jakich typów się spodziewać (na przykład, gdy metoda zwraca typ unii ), możesz użyć strażników typów.

Na przykład, dla typów pierwotnych możesz użyć wartownika typeof :

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

W przypadku typów złożonych możesz użyć instancji ochrony :

if (thing instanceof Array) {
  // Do stuff
}
Cocowalla
źródło
Chyba dlatego, że twoja odpowiedź nie jest związana z pytaniem. Pytanie polegało na tym, aby nazwa klasy nie warunkowo Robiła rzeczy na typie instancji.
Daniel Leiszen