Get class method name in TypeScript

To view this question, this question is similar to the following:

How do I get the type name of an object in JavaScript? Get runtime class name of an object in TypeScript

However, this is a little different.

I am looking to get the name of a method that belongs to a class and store it in a variable in TypeScript / JavaScript.
Take a look at the following setting:

class Foo { bar(){ // logic } } 

The above value has a TypeScript value, and I would like to create a method in another class that will return the bar() method name to me, i.e. "bar"
eg:

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

I would like to use ClassHelper as follows:

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

I looked through many 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 because my methods do not start with 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 name of the class, but when reading messages it seems that using constructor may be unreliable.

Also, when I debugged the code using some of these methods, passing the method as follows: ClassHelper.getMethodName(foo.bar); will result in passing the parameter if the method accepts one, for example:

 class Foo { bar(param: any){ // logic } } var foo = new Foo(); var barName = ClassHelper.getMethodName(foo.bar); // results in param getting passed through 

I struggled with this for a while, if someone has information on how I can solve this, we will be very grateful.

My .toString() in the method passed returns this:

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

but not:

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

and according to MDN it is not intended:

That is, toString decompiles this function, and the returned string includes the function keyword, argument list, curly braces and the source of the function body.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description

+7
javascript regex typescript
source share
5 answers

I have found a solution. I'm not sure how effective and reusable it is, but it worked in several test cases, including nested methods, for example Class -> Class -> Method

My decision:

 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 notation ClassHelper.getMethodName(() => foo.bar); was the key to making this work, as it allowed .toString() contain return foo.bar;

The next thing I needed to do was extract the method call from .toString() , after which I used the array and string functions to return the last substring, which inevitably is the name of the method.

As I said, this is probably not the most elegant solution, but it worked and even worked for nested methods

NOTE. You can replace the lambda function with a regular anonymous function

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

I accepted John White's idea and improved it to work for every occasion I could think of. The advantage of this method is that you do not need to parse the js code at run time. However, there is an extreme case where it simply cannot display the correct property name, because there are several property names for the right.

 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)); 
+2
source share

Unfortunately, the method name of the Typescript class is lost when compiling in JS (as you correctly inferred). Typescript methods are compiled into Javascript, adding this method to the prototype of the Javascript class. (Check out compiled Javascript for more information).

In Javascript, this works:

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

So, you could think about changing the prototype of the class, so the class itself becomes the key to the object:

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

Of course, this is a very hacky decision, since

  • Complexity O(N) (loop through an array)
  • I am not sure if this works in every case.

(working) An 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" 

A better solution would be a Typescript compiler option, which changes the way Typescript translates classes into javascript. However, I currently don't know a single option that does such things.

+1
source share

Object.keys can help you. 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 
+1
source share

You can pass both the object and the method, get an array of property keys on the object, and then use the property keys to see if each given property is the same object reference as the method provided, and if so, 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; } } 

It has the disadvantage that the host object must also be known. If the method is not found, undefined returned.

+1
source share

All Articles