在 TypeScript 中获取类方法的名称

Posted

技术标签:

【中文标题】在 TypeScript 中获取类方法的名称【英文标题】:Get name of class method in TypeScript 【发布时间】:2016-11-02 06:08:27 【问题描述】:

对于查看此问题的任何人,此问题类似于以下内容:

How do I get the name of an object's type in javascript?Get an object's class name at runtime in TypeScript

但在某些方面有所不同。

我正在寻找属于某个类的方法的名称,并将其存储在 TypeScript/JavaScript 中的变量中。 看看下面的设置:

class Foo 

    bar()
        // logic
    


以上是有效的 TypeScript,我想在另一个类中创建一个方法,该方法将返回 bar() 方法的名称,即 "bar" 例如:

class ClassHelper 

    getMethodName(method: any)
        return method.name; // per say
    


然后我希望能够通过以下方式使用ClassHelper

var foo = new Foo();
var barName = ClassHelper.getMethodName(foo.bar); // "bar"

我看过很多帖子,有些建议使用以下内容:

var funcNameRegex = /function (.1,)\(/;   
var results = (funcNameRegex).exec(obj.toString());
var result = results && results.length > 1 && results[1];

但这失败了,因为我的方法不是以function开头的 另一个建议是:

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

这只会返回类名,但是从阅读帖子来看,使用constructor 似乎是不可靠的。

另外,当我使用其中一些方法调试代码时,像这样传递方法:ClassHelper.getMethodName(foo.bar); 将导致传递参数,如果该方法采用一个,例如:

class Foo 

    bar(param: any)
        // logic
    



var foo = new Foo();
var barName = ClassHelper.getMethodName(foo.bar); // results in param getting passed through

我已经为此苦苦挣扎了一段时间,如果有人能提供有关我如何解决此问题的任何信息,将不胜感激。

我在传入的方法上的.toString() 返回:

.toString() = "function (param)  // code "

而不是:

.toString() = "function bar(param)  // code "

根据 MDN,它也不应该这样做:

即toString对函数进行反编译,返回的字符串包括函数关键字、参数列表、大括号、函数体的来源。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description

【问题讨论】:

【参考方案1】:

您好,我一直在进行一些测试,并找到了一个更简单的解决方案。

class Parent
  constructor()
    // here you initialize your stuff
  
   parentMethod1 () return "I am method 1" 
   parentMethod2 () return "I am method 2" 




class Child extends Parent
  constructor()
    // make this if you want to pass extra args to parent class
    super()
  
  childMethod1()  /*child actions*/ 
  childMethod2() /* other child actions */ 


const parent = new Parent();
const child = new Child();

console.log( Object.getOwnPropertyNames(parent.__proto__)) // --> ["constructor", "parentMethod1", "parentMethod2"]
console.log(Object.getOwnPropertyNames(child.__proto__)) //--> ["constructor", "childMethod1","childMethod2"]

console.log(parent.constructor.name) // --> Parent
console.log(child.constructor.name) // --> Child

【讨论】:

【参考方案2】:

我找到了解决方案。我不确定它的效率和可重用性如何,但它适用于多个测试用例,包括嵌套方法,例如Class -> Class -> Method

我的解决方案:

class ClassHelpers 
    getName(obj: any): string     
        if (obj.name) 
            return obj.name;
        

        var funcNameRegex = /function (.1,)\(/;   
        var results = (funcNameRegex).exec(obj.toString());
        var result = results && results.length > 1 && results[1];

        if(!result)
            funcNameRegex = /return .([^;]+)/;
            results = (funcNameRegex).exec(obj.toString());
            result = results && results.length > 1 && results[1].split(".").pop();
        

        return result || "";
    



class Foo 

    bar(param: any)
        // logic
    



var foo = new Foo();
var barName = ClassHelper.getMethodName(() => foo.bar);

lambda 符号 ClassHelper.getMethodName(() => foo.bar); 是让它工作的关键,因为它允许 .toString() 包含 return foo.bar;

接下来我要做的是从.toString() 中提取方法调用,然后我使用数组和字符串函数返回最后一个子字符串,这不可避免地是方法名称。

就像我说的,它可能不是最优雅的解决方案,但它确实有效,甚至适用于嵌套方法

注意:您可以将 lambda 函数替换为常规匿名函数

var foo = new Foo();
var barName = ClassHelper.getMethodName(function()  return foo.bar; );

【讨论】:

当涉及到旧浏览器和保留关键字时,此解决方案可能会有缺点,因为 lambda 可能会编译为 foo["default"]。否则看起来不错。 您可以将 lambda 函数更改为我相信的常规函数​​:function () return foo.bar; 【参考方案3】:

我采纳了 John White 的想法并对其进行了改进,使其适用于我能想到的所有情况。这种方法的优点是运行时不需要解析js代码。但是有一个极端情况,它根本无法推断出正确的属性名称,因为有多个正确的属性名称。

class Foo 
  bar() 
  foo() 


class ClassHelper 
  static getMethodName(obj, method) 
    var methodName = null;
    Object.getOwnPropertyNames(obj).forEach(prop => 
      if (obj[prop] === method) 
        methodName = prop;
      
    );

    if (methodName !== null) 
      return methodName;
    
    
    var proto = Object.getPrototypeOf(obj);
    if (proto) 
      return ClassHelper.getMethodName(proto, method);
    
    return null;
  


var foo = new Foo();
console.log(ClassHelper.getMethodName(foo, foo.bar));
console.log(ClassHelper.getMethodName(Foo.prototype, foo.bar));
console.log(ClassHelper.getMethodName(Foo.prototype, Foo.prototype.bar));

var edgeCase =  bar(), foo() ;
edgeCase.foo = edgeCase.bar;
console.log(ClassHelper.getMethodName(edgeCase, edgeCase.bar));

【讨论】:

【参考方案4】:

Object.keys 可以帮助你。 试试这个方法

class Greeter 
    test : Function;

    constructor(message: string) 

        this.test = function()
            return message;
        
        


var a = new Greeter("world");

var properties = Object.keys(a);
alert(properties.join(', ')); //test

【讨论】:

【参考方案5】:

您可以同时传入对象和方法,获取对象上的属性键数组,然后使用属性键查看每个给定属性是否与提供的方法相同的对象引用——如果是,则存在一场比赛。

class ClassHelper 
    getMethodName(obj: any, method: any) 
        var methodName: string;

        if (method) 
            Object.keys(obj).forEach(key => 
                if (obj[key] === method) 
                    methodName = key;
                
            );
        

        return methodName;
    

这有一个缺点,即主机 object 也必须是已知的。如果找不到方法,则返回undefined

【讨论】:

在尝试这种方法后,我的单元测试失败了,因此没有为我的具体情况提供合适的答案,但这是一种很好的方法,可能对其他人有用 @Colum 可能是同一个方法存在于多个属性中,还是涉及到继承?对于后者,您可以遍历原型链直到到达Object【参考方案6】:

不幸的是,Typescript 类方法的名称在编译为 JS 时丢失了(正如您正确推断的那样)。通过将方法添加到 Javascript 类的原型中,将 Typescript 方法编译为 Javascript。 (查看编译后的 Javascript 以获得更多信息)。

在 Javascript 中,这是可行的:

Foo.prototype["bar"] // returns foo.bar <<the function>>

所以,你能想到的就是把类的原型倒过来,让类本身成为对象的key:

Foo.prototype[foo.bar] // return "bar"

当然,这是一个非常老套的解决方案,因为

    复杂度为O(N)(循环遍历数组) 我不确定这是否适用于所有情况。

(工作)您的问题的示例:

class Foo
    bar()


class ClassHelper
    static reversePrototype(cls:any)
        let r = ;
        for (var key in cls.prototype)
            r[cls.prototype[key]] = key;
        
        return r;
    

    static getMethodNameOf(cls: any, method:any):string
        let reverseObject = ClassHelper.reversePrototype(cls);
        return reverseObject[method];
    


var foo = new Foo();
console.log(ClassHelper.getMethodNameOf(Foo, foo.bar)) // "bar"

更好的解决方案是一个 typescript 编译器选项,它可以改变 typescript 将类转换为 javascript 的方式。但是,我目前不知道有任何选项可以做这种事情。

【讨论】:

这是一个很好的解决方法,但是如果类未知,它就不起作用。这可能看起来是落后和错误的,但是在我的代码中实现这种方式的方式是,一个单独的类采用该方法的一个参数,然后它会尝试查找名称。参数可以是任何类的方法。我想我可能已经找到了解决方案,我目前正在研究它 不要忘记,当您将函数作为键放入对象时,它会转换为字符串表示形式。这使得具有相同内容的不同功能发生冲突。

以上是关于在 TypeScript 中获取类方法的名称的主要内容,如果未能解决你的问题,请参考以下文章

如何在 TypeScript 中获取类方法的返回类型

TypeScript 中是不是有在类中使用的关键字来获取其类型?

在TypeScript中获取调用属性的名称

如何在 TypeScript 类中正确声明计算属性名称?

在 TypeScript 中获取实例的类

如何在循环中发送http获取方法Typescript。