简体   繁体   English

在 TypeScript 中获取类方法的名称

[英]Get name of class method in TypeScript

For anyone viewing this, this question is similar to the following:对于查看此内容的任何人,此问题类似于以下内容:

How do I get the name of an object's type in JavaScript? 如何在 JavaScript 中获取对象类型的名称?
Get an object's class name at runtime in TypeScript 在运行时在 TypeScript 中获取对象的类名

However it is different in a few regards.然而,它在一些方面有所不同。

I'm looking to get the name of method that belongs to a class and store it in a variable in TypeScript / JavaScript.我正在寻找属于一个类的方法的名称并将其存储在 TypeScript/JavaScript 中的变量中。
Take a look at the following setup:看看下面的设置:

class Foo {

    bar(){
        // logic
    }

}

The above is valid TypeScript and I would like to create a method in a different class that will return me the name of the bar() method, ie "bar"以上是有效的 TypeScript,我想在不同的类中创建一个方法,该方法将返回bar()方法的名称,即"bar"
eg:例如:

class ClassHelper {

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

}

I would then like to be able to use the ClassHelper in the following way:然后,我希望能够通过以下方式使用ClassHelper

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

I've looked at a lot of posts, some suggest using the following:我看过很多帖子,有些人建议使用以下内容:

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

but this fails as my methods do not begin with function但这失败了,因为我的方法不是以function开头
another suggestion was:另一个建议是:

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

This only returns the class name however and from reading posts, it seems using constructor can be unreliable.这仅返回类名,但是从阅读帖子来看,使用constructor似乎不可靠。

Also, when I've debugged the code using some of these methods, passing in the method like so: ClassHelper.getMethodName(foo.bar);此外,当我使用其中一些方法调试代码时,像这样传入方法: ClassHelper.getMethodName(foo.bar); will result in the parameter being passed if the method takes one, eg:如果方法采用一个,将导致参数被传递,例如:

class Foo {

    bar(param: any){
        // logic
    }

}

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

I've been struggling with this for a while, if anyone has any information on how I can solve this it would be greatly appreciated.我已经为此苦苦挣扎了一段时间,如果有人有任何有关我如何解决此问题的信息,将不胜感激。

My .toString() on the method passed in returns this:我在传入的方法上的.toString()返回:

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

rather than:而不是:

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

and according to MDN it isn't supposed to either:根据 MDN,它也不应该:

That is, toString decompiles the function, and the string returned includes the function keyword, the argument list, curly braces, and the source of the function body.即 toString 对函数进行反编译,返回的字符串包括函数关键字、参数列表、花括号和函数体的来源。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description

I have taken John White's idea and improved it so it works for every case I could think of.我采用了约翰怀特的想法并对其进行了改进,因此它适用于我能想到的每种情况。 This method has the advantage of not needing to parse js code at runtime.这种方法的优点是不需要在运行时解析js代码。 There is an edge case though, where it simply can't deduce the right property name because there are multiple right property names.但是有一个边缘情况,它根本无法推导出正确的属性名称,因为有多个正确的属性名称。

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

I found a solution.我找到了解决方案。 I'm not sure how efficient and reusable it is, but it worked in multiple test cases, included nested methods, eg Class -> Class -> Method我不确定它的效率和可重用性如何,但它适用于多个测试用例,包括嵌套方法,例如Class -> Class -> Method

My solution:我的解决方案:

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

The lambda notation ClassHelper.getMethodName(() => foo.bar); lambda 符号ClassHelper.getMethodName(() => foo.bar); was key to getting this to work as it allowed the .toString() to contain return foo.bar;是让它工作的关键,因为它允许.toString()包含return foo.bar;

The next thing I had to do was to extract the method call from the .toString() then I used array and string functions to return the last substring which inevitably is the method name.接下来我要做的是从.toString()提取方法调用,然后我使用数组和字符串函数返回最后一个子字符串,它不可避免地是方法名称。

Like I said, it's probably not the most elegant solution but it has worked and even worked for nested methods就像我说的,它可能不是最优雅的解决方案,但它已经工作,甚至适用于嵌套方法

NOTE: You can replace the lambda function with a regular anonymous function注意:您可以使用常规匿名函数替换 lambda 函数

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

Unfortunately, the name of Typescript class methods is lost when compiling to JS (as you correctly inferred).不幸的是,在编译为 JS 时,Typescript 类方法的名称丢失了(正如您正确推断的那样)。 Typescript methods are compiled to Javascript by adding the method to the prototype of a Javascript class.通过将方法添加到 Javascript 类的原型中,将 Typescript 方法编译为 Javascript。 (Check the compiled Javascript for more info). (检查已编译的 Javascript 以获取更多信息)。

In Javascript, this works:在 Javascript 中,这有效:

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

So, the thing you could think of is reversing the prototype of the class, so the class itself becomes the key of the object:所以,你能想到的就是反转类的原型,让类本身成为对象的关键:

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

Of course, this is a very hacky solution, since当然,这是一个非常hacky的解决方案,因为

  1. Complexity is O(N) (loop through array)复杂度为O(N) (循环遍历数组)
  2. I'm not sure this works in every case.我不确定这在每种情况下都有效。

(Working) Example for your problem: (工作)您的问题示例:

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"

The better solution would be a typescript compiler option that changes the way typescript transpiles classes to javascript.更好的解决方案是使用 typescript 编译器选项来改变 typescript 将类转换为 javascript 的方式。 However, I'm currently not aware of any option that does this sort of thing.但是,我目前不知道有任何选项可以执行此类操作。

Object.keys may help you. Object.keys可以帮到你。 Try this way试试这个方法

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

You could pass in both the object and the method, get the array of property keys on the object, then using property keys see if each given property is the same object reference as the supplied method -- and if so, there is a match.您可以传入对象和方法,获取对象上的属性键数组,然后使用属性键查看每个给定的属性是否与提供的方法是相同的对象引用——如果是,则存在匹配。

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

This has the downside that the host object must also be known.这样做的缺点是还必须知道宿主对象 If the method cannot be found, undefined will be returned.如果找不到该方法,则返回undefined

Hi I have been making some testing and I founded a simpler solution.嗨,我一直在做一些测试,我建立了一个更简单的解决方案。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM