20

I want to get the name of the current method from within an instance method of a class in Typescript.

(Pseudocode, doesn't work):

class Foo {
    bar() {
        console.log(something); //what should something be?
    }
}

new Foo().bar();

I expect 'something' to return 'bar'. I realize that this can give me the class, and I could somehow get the class and its attributes from it, but I do not know how to get 'this function' (i.e, the method bar, not the class Foo).

I have seen several other questions related to finding the class name, etc. but not one that addresses getting the current method name.

y2bd
  • 5,783
  • 1
  • 22
  • 25
Anand
  • 3,690
  • 4
  • 33
  • 64
  • @Rienk - I am trying to do something more complex (specifically, ruby's method_missing behavior in typescript) , but as a simpler example, assume I have legacy code with (a large number of methods) like `getFoo()` and `getBar()`, and I want to handle all these using a `getObject(objName)` function that parses the function name, and returns the object from a dictionary, by name. I want to call `getObject(thisFuncName)` in every such function rather than having to change each such function to say things like `getObject('foo')`, etc.. – Anand Aug 17 '17 at 21:07
  • 2
    There is [arguments.callee.name](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee), but it does not work in strict mode, and class methods are executed in strict mode. You could try to use [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) for that, but not all browsers support proxies.. – artem Aug 17 '17 at 21:09
  • @Kokodoko I have added a javascript tag – Anand Aug 17 '17 at 21:11
  • @lilezek this is a fairly common feature in dynamic languages - ruby provides an \_\_method\_\_ to return current method name from within each method - see https://stackoverflow.com/questions/199527/get-the-name-of-the-currently-executing-method – Anand Aug 17 '17 at 21:12
  • you can call the function with parameterized name (i think): var instance = new Foo(); instance[methodNameParameter](); – R_Ice Aug 17 '17 at 21:13

8 Answers8

21

Besides the arguments.callee.name there is no straightforward way of getting this.

I propose 2 other methods:

Use decorators to inject the method name:

function annotateName(target, name, desc) {
    var method = desc.value;
    desc.value = function () {
        var prevMethod = this.currentMethod;
        this.currentMethod = name;
        method.apply(this, arguments);
        this.currentMethod = prevMethod;   
    }
}

class Foo {
    currentMethod: string;

    @annotateName
    bar() {
        alert(this.currentMethod);
        this.tux();
        alert(this.currentMethod);
    }

    @annotateName
    tux() {
        alert(this.currentMethod);
    }
}

new Foo().bar();

The downside is that you have to annotate all the functions you want to get the name from. You could instead just annotate the class and in the decorator you would iterate over all prototype functions and apply the same idea.


My second option is not standardised and need more care to get consistent results across browsers. It relies on creating an Error object and getting it's stack trace.

class Foo {
    bar() {
        console.log(getMethodName());    
    }
}

function getMethodName() {
    var err = new Error();
    return /at \w+\.(\w+)/.exec(err.stack.split('\n')[2])[1] // we want the 2nd method in the call stack

}

new Foo().bar();
Cristi Mihai
  • 2,505
  • 1
  • 19
  • 20
  • 2
    please explain where you get `this` and `arguments` from in your `annotateName` function. – luukvhoudt Aug 10 '19 at 19:09
  • @luukvhoudt Both `this` and `arguments` are special variables within functions in javascript. Check out https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments respectively. – Gerard van Helden May 07 '21 at 15:48
3

Not sure if this would help, but:

class Foo {
    bar() {
        console.log(Object.getOwnPropertyNames(Foo.prototype)); // ["constructor", "bar"]
    }
}

new Foo().bar();
Slai
  • 22,144
  • 5
  • 45
  • 53
3

Here is my solution to get the method name.

  /**
   * @description Get log invoker name
   * @return {string} The invoker name
   */
  private static callerName(): string {
    try {
      throw new Error();
    } catch (e) {
      try {
        return e.stack.split('at ')[3].split(' ')[0];
      } catch (e) {
        return '';
      }
    }
  }
Avi Siboni
  • 686
  • 7
  • 16
1

Keep in mind that during compilation and minification you might lose the actual name of what you're trying to use. You might consider looking into the ts-nameof babel macro that reads the name of virtually anything during compilation and returns it's actual string representation.

Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74
1

I was looking for a solution as well, try this:

class Foo {
  bar() {
      console.log(this.bar.name); // <-- Print out the function name.
  }
}
  
new Foo().bar(); 

What is nice is that you'll get an error if you change the function name, but forget to update the console statement.

Chris Livdahl
  • 4,662
  • 2
  • 38
  • 33
0

for class name - Foo.name for method name - this.bar.name

  • 4
    The question was regarding how to get the name of the _currently-executing_ method. – Troy May 01 '19 at 12:16
0

Just to answer the question with another interesting take, you could do (but shouldn't do) something like this:

class Foo {
    constructor(private http: HttpClient) {

        const apiUrl = 'http://myapi.com/api/';

        {
            const functionName = 'getBar';
            this[functionName] = function () {
                return http.get(apiUrl + functionName);
            }
        }

        {
            const functionName = 'postBar';
            this[functionName] = function () {
                return http.get(apiUrl + functionName);
            }
        }

        {
            const functionName= 'putBar';
            this[functionName] = function () {
                return http.get(apiUrl + functionName);
            }
        }

        {
            const functionName= 'deleteBar';
            this[functionName] = function () {
                return http.get(apiUrl + functionName);
            }
        }
    }
}

It certainly is not an elegant solution, and I can't really imagine a good use case for doing something like this, as I'm pretty sure the compiler doesn't recognize new Foo(http).deleteBar(). Maybe someone can come up with an elegant solution with this idea, I'll leave that as an experiment for someone.

But with this pattern (if you employ some kind of devops scaffolding or "strong copy-paste skills") you can always access your method's name via functionName

William Herrmann
  • 343
  • 2
  • 10
0

function getFunctionName() {
   return getFunctionName.caller.name
}

function foobar() {
  console.log(getFunctionName())
}

foobar() // logs 'foobar' as the currently running function

You can use the .caller property:

A Function object's caller property accessor property represents the function that invoked the specified function. For strict, async function, and generator function callers, accessing the caller property throws an exception.

Although non standard, in my experience the caller property is supported everywhere I have used it (mostly node.js). Check for compatibility before using it. I have only every used it for debugging purposes. For more information, please see

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

Rob
  • 1,576
  • 3
  • 22
  • 52