【问题标题】:Get an object's class name at runtime在运行时获取对象的类名
【发布时间】:2012-11-16 19:16:20
【问题描述】:

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

class MyClass{}

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

【问题讨论】:

  • here。在运行时,您正在运行 JavaScript。
  • 你如何在 TypeScript 文件中获取构造函数名称?您不能在 TypeScript 方法(在 .ts 文件中)中执行 this.constructor.name。

标签: typescript


【解决方案1】:

看到这个question

由于 TypeScript 被编译为 JavaScript,因此在运行时您正在运行 JavaScript,因此将适用相同的规则。

【讨论】:

    【解决方案2】:

    完整的 TypeScript 代码

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

    【讨论】:

    • 如果您最小化和优化您的 typescript/javascript 代码,您可能会遇到一些问题。它可能会更改函数名称,然后您的类名比较可能是错误的。
    【解决方案3】:

    我知道我迟到了,但我发现这也行。

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

    或者...

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

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

    希望这会有所帮助。

    【讨论】:

    • 对不起,当然。通常,您使用缩小、丑化和其他后处理系统。所以在生产服务器上你的类名将不一样。而且您的代码将不起作用。我没有找到真正好的解决方案来获取类名。最合适的方法是用你的类名定义一个静态变量。
    【解决方案4】:

    简单回答:

    class MyClass {}
    
    const instance = new MyClass();
    
    console.log(instance.constructor.name); // MyClass
    console.log(MyClass.name);              // MyClass
    

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

    【讨论】:

    • 不幸的是 MyClass.name 是 ES6 特性,因此它在 IE11 中不起作用。
    • typescript 将对此抛出错误。应该做let instance: any = this.constructor; console.log(instance.name);
    • @Subash 一种避免强制转换为 any 的简洁方法是 console.log(instance.constructor['name']);
    • 如果您要缩小代码,MyClass.name 将无法正常工作。因为它会缩小类的名称。
    • @Henry 不是我所知道的。缩小将真正重命名该类。根据您要实现的目标,在运行时使用构造函数名称可能不是最佳策略。
    【解决方案5】:

    在 Angular2 中,这有助于获取组件名称:

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

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

    【讨论】:

    • 但是,如果你缩小/丑化你的代码,这将不起作用
    • 要获得组件的可用“名称”,最好获得element.nativeElement 的标签名称 - 在指令上,您可以像@Optional() element: ElementRef<HTMLElement> 这样获得组件的名称,然后使用@987654324 @
    • (标签名称不会缩小)
    【解决方案6】:

    您需要先将实例强​​制转换为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"
    

    【讨论】:

    • 在 TypeScript 2.6.2 上尝试过,我收到了这个错误:Property 'name' does not exist on type 'Function'.
    • (this as {}).constructor.name(this as object).constructor.nameany 更好,因为那样你实际上会获得自动完成功能:-)
    【解决方案7】:

    我的解决方案是不要依赖类名。 object.constructor.name 理论上有效。但是如果你在 Ionic 之类的东西中使用 TypeScript,一旦你投入生产,它就会火上浇油,因为 Ionic 的生产模式会缩小 Javascript 代码。所以类被命名为“a”和“e”。

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

    export class Person {
    id: number;
    name: string;
    typeName: string;
    
    constructor() {
    typeName = "Person";
    }
    

    是的,这不是被问到的,真的。但是在可能会被缩小的东西上使用constructor.name只是在乞求头痛。

    【讨论】:

    • 代码缩小后的事件,我很确定你仍然可以做`let instance=new Person (); (instance.constructor.name==Person.name)这两个名字应该被缩小为同一件事。
    • @ChristopherChase 预计这两个名称将被缩小为相同的东西,但通常情况下,它会是 shortnon-unique 之类的东西abc 等,所以你不应该依赖这个。
    • 我认为对于大多数查找此问题的人来说,这可能是正确的答案。我认为重要的是要注意 Typescript “存在”的位置,并厌倦在运行时依赖它。直接在数据中使用类名是非常容易/诱人的(至少对我来说),但问问自己:在这一点上,数据是代表类,还是类代表数据?我不是告诉你它总是必须是一种方式或另一种方式,但我是说你必须选择一个并坚持下去,否则你会追逐你的尾巴。
    • 此解决方案将更容易跨前端和后端实施,因为缩小不会影响它。
    【解决方案8】:
    • 必须添加“.prototype.”才能使用:myClass.prototype.constructor.name.
    • 否则使用以下代码: myClass.constructor.name,我遇到了 TypeScript 错误:

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

    【讨论】:

      【解决方案9】:

      如果您已经知道预期的类型(例如,当一个方法返回 union type 时),那么您可以使用类型保护。

      例如,对于原始类型,您可以使用typeof guard

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

      对于复杂类型,您可以使用instanceof guard

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

      【讨论】:

      • 我猜是因为您的回答与问题无关。问题是让类名不要有条件地对实例类型进行操作。
      【解决方案10】:

      使用Decorators 的解决方案可以在缩小/丑化中幸存

      我们使用代码生成来用元数据装饰我们的实体类,如下所示:

      @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 反射 API 编写的 pollyfill
      • JS中装饰器的提案可以是tracked here

      【讨论】:

      • 您好,感谢您的解决方案!但是尝试使用您的装饰器时,我收到“Reflect.metadata 不是函数”错误。为了解决这个“反射元数据”包必须安装(npmjs.com/package/reflect-metadata)你能把这个信息整合到你的答案中吗?
      • @lbrutti 你必须先导入反射元数据包。代码导入“reflect-metadata”;进入源文件的顶部。然后,使用反射。
      • @WooyoungTylerKim 这就是我所做的;)我只是要求在答案中强调这一点,以使其更加有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-20
      • 2019-07-30
      相关资源
      最近更新 更多