tcoder01
tcoder01

Reputation: 169

Get class methods in typescript

I am running typescript unit tests with mocha chai (after setting the compiler options to ts-node).

In one of my unit tests, I would like to get all the methods of a utility class that I have created and run the same tests on them. To be more concrete, I would like to achieve something like this:

UtilityClass.getMethods().forEach(method=>{method(sameInputData)})

Is there a way to implement getMethods elegantly? Or maybe, another way to tackle this need?

Upvotes: 11

Views: 28236

Answers (3)

Andry
Andry

Reputation: 16845

You forget that TypeScript is Javascript. Remember that the TypeScript compiler compiles your code into Javascript.

So, as you normally do in Javascript, you can enumerate members on an object like this:

UtilityClass myclass = ...;

for (var member in myclass) { /* do something */ }

More advanced

If you want to make sure you don't get inherited members:

for (var member in myclass) {
  if (myclass.hasOwnProperty(member)) {
    /* do something */
  }
}

Extract methods

If you want to make sure you get only methods (functions):

for (var member in myclass) { // For each member of the dictionary
  if (typeof myclass[member] == "function") { // Is it a function?
    if (myclass.hasOwnProperty(member)) { // Not inherited
      // do something...
    }
  }
}

Reflection in TypeScript

As you can see the approaches require an instance to work on. You don't work on the class. Reflection is what you are trying to achieve in the context of OOP; however Javascript (which is not OOP) deals with it in a different way.

Upvotes: 11

csga5000
csga5000

Reputation: 4141

I had a very hard time getting other answers to work. They also don't cover a way to do it without an instance, if that's helpful to you as it was for me.

This is what I came up with:

SomeClass.ts

import { route } from "../../lib/route_decorator";

export class SomeClass {
    index() {
        console.log("here");
    }
}

And somefile.ts

let ctrl = require("./filepath/filename");
// This is because angular exports as `exports.SomeClass = SomeClass;`
ctrl = ctrl[Object.keys(ctrl)[0]];
let ctrlObj = new ctrl();

// Access from Class w/o instance
console.log(Reflect.ownKeys(ctrl.prototype));
// Access from instance
console.log(Reflect.ownKeys(Object.getPrototypeOf(ctrlObj)));

This works, outputting:

[ 'constructor', 'index' ]
[ 'constructor', 'index' ]

Upvotes: 13

Giovanni P.
Giovanni P.

Reputation: 1047

Andry's answer works well for ES3/ES5, but things get weird if you use ES6 and inheritance...

Here is a little more complex example, and how to retrieve each type of method in case of ES6:

class ParentTest {
    parentFoo = function() {
        // do stuff
    }

    parentBar() {
        // do stuff
    }
}

class Test extends ParentTest {
    foo = function() {
        // do stuff
    }

    bar() {
        // do stuff
    }
}

let instance = new Test();

// These two works equally for every method declared as a property of type "function"
for(let method in instance) {
    console.log(method); // foo, foo2
}
console.log(Object.keys(instance)); // Array [ "parentFoo", "foo" ]

// This works for method of the class
let protoOfTest = Object.getPrototypeOf(instance);
console.log(Object.getOwnPropertyNames(protoOfTest)); // Array [ "constructor", "bar" ] 

// This works for methods of the extended class
let protoOfParentTest = Object.getPrototypeOf(protoOfTest);
console.log(Object.getOwnPropertyNames(protoOfParentTest)); // Array [ "constructor", "parentBar" ]

Upvotes: 8

Related Questions