在运行时获取对象的类名

447

在运行时使用TypeScript获取对象的类/类型名称是否可行?

class MyClass{}

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

5
请点击这里。在运行时,您正在运行 JavaScript。请查阅该链接以获取更多信息。 - Matt Burland
4
在 TypeScript 文件中,如何获取构造函数的名称?你不能在 TypeScript 方法(.ts 文件中)使用 this.constructor.name。 - kind_robot
接口的名称呢? - Rvanlaak
11个回答

732

简单回答:

class MyClass {}

const instance = new MyClass();

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

但是请注意,在使用压缩代码时,名称很可能会有所不同。


15
很遗憾,MyClass.name是ES6的一个特性,因此在IE11中无法使用。 - begie
15
TypeScript 会在这里抛出错误。 应该这样做:let instance: any = this.constructor; console.log(instance.name); - Subash
12
避免强制转换为any的更简洁方法是console.log(instance.constructor['name']); - Nick Strupat
15
如果你压缩代码,MyClass.name 将无法正常工作,因为它会将类的名称进行压缩。 - AngryHacker
3
不是我所知道的,代码压缩确实会重命名类名。根据你想实现的目标,使用构造函数名称可能不是最佳策略。 - Mikael Couzic
显示剩余8条评论

41

我的解决方案不是依赖于类名。object.constructor.name原理上可行。但如果你在像Ionic这样的TypeScript项目中使用,当进入生产环境时就会出问题,因为Ionic的生产模式会缩小JavaScript代码。所以类的名称会变成“a”和“e”之类的东西。

我最终做的是,在我所有的对象中都添加一个typeName类,构造函数将类名分配给它。所以:

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

constructor() {
typeName = "Person";
}

是的,那并不是真正想要问的。但在可能被压缩的情况下使用 constructor.name 只会让人头痛。


6
在代码被压缩后,我相信你仍然可以执行 let instance=new Person(); (instance.constructor.name==Person.name) 这段代码。两个名称都应该被压缩为相同的内容。 - Christopher Chase
1
@ChristopherChase 预计两个名称将被缩短为相同的内容,但通常会是一些短小而不唯一的东西,例如abc等,因此您不应依赖于此。 - tooleks
1
我认为这可能是大多数人寻找答案的正确答案。我认为重要的是要注意TypeScript“存在”的位置,并谨慎依赖它在运行时。直接在数据中使用类名非常容易/诱人(至少对我来说),但请问自己:此时,数据代表类,还是类代表数据?我不是告诉你它总是必须是一种方式或另一种方式,但我是说你必须选择一种并坚持下去,否则你将会一直在追逐自己的尾巴。 - theaceofthespade
这个解决方案将更容易在前端和后端实现,因为缩小不会影响它。 - theaceofthespade

31

我知道我来晚了,但我发现这也有效。

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

或者...

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

以上代码将整个构造函数代码作为字符串获取,并应用正则表达式以获取所有“单词”。第一个单词应该是“function”,第二个单词应该是类的名称。

希望这可以帮助到您。


9
抱歉,当然。 通常,您会使用缩小、丑化和其他后处理系统。 因此,在生产服务器上,您的类名将不同。 而且你的代码将无法工作。 我没有找到真正好的解决方案来获取类名。 最适合的方法是定义一个包含您的类名的静态变量。 - Dima Kurilo

26
你需要先将实例转换为 any,因为 Function 的类型定义中没有 name 属性。
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"

更新:

使用 TypeScript 2.4(甚至是更早版本),代码可以更加简洁:

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"

3
尝试在 TypeScript 2.6.2 上运行时,我遇到了这个错误:Property 'name' does not exist on type 'Function'。 - orad
(this as {}).constructor.name(this as object).constructor.nameany 更好,因为你实际上可以获得自动完成 :-) - Simon_Weaver

19

请参见此问题

TypeScript被编译为JavaScript,因此在运行时你正在运行JavaScript,所以同样的规则适用。


17

使用装饰器的解决方案,可在最小化/丑化后生存

我们使用代码生成来为实体类添加元数据,如下所示:

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

然后使用以下辅助函数消费:

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

额外信息:

  • 您可能需要安装reflect-metadata
  • reflect-metadata是由TypeScript成员编写的用于提议的ES7 Reflection API的polyfill。
  • JS中修饰器的提案可以在此处跟踪

1
你好,感谢这个解决方案!但是在尝试使用你的装饰器时,我遇到了“Reflect.metadata is not a function”错误。为了解决这个问题,需要安装“reflect-metadata”包(https://www.npmjs.com/package/reflect-metadata)。请问能否将此信息集成到您的答复中? - lbrutti
@lbrutti,你必须先导入 reflect-metadata 包。将代码 import "reflect-metadata"; 放在你的源文件顶部,然后使用 reflect。 - Wooyoung Tyler Kim
1
@WooyoungTylerKim,那就是我所做的;)我只是在问是否可以在答案中突出这一点,使其更加有用。 - lbrutti

7
在Angular2中,可以使用以下代码获取组件名称:
    getName() {
        let comp:any = this.constructor;
        return comp.name;
    }

comp:any 是必需的,因为 TypeScript 编译器会发出错误,因为 Function 最初没有 name 属性。


9
如果您对代码进行缩小/混淆,这将无法起作用。 - Admir Sabanovic
1
要获取组件的可用“名称”,最好获取element.nativeElementtagName。在指令上,您可以像这样获取组件的名称:@Optional() element: ElementRef<HTMLElement>,然后使用if (element != null && element.nativeElement.tagName.startsWith('APP-')) { this.name = element.nativeElement.tagName; } - Simon_Weaver
(标签名称不缩短) - Simon_Weaver

7
  • 必须添加“.prototype.”才能使用:myClass.prototype.constructor.name
  • 否则,使用以下代码:myClass.constructor.name,我会遇到TypeScript错误:

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


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

5
如果你对TypeScript/JavaScript代码进行了最小化和优化,可能会遇到一些问题。这会改变函数名称,从而导致类名比较出错。 - Antti

0

我不知道从什么时候开始这是可能的,但现在它是:

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;

感谢本教程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接