0

Supposed that there is a need to use conditional which do the same thing but calling a different method.

const c = true; // for example sake 
let b = '';
if (c) {
  b = 'method1';
} else {
  b = 'method2';
}

function a(){
   this.service.b(filter).subscribe((result) =>{
      // stuff happens
   });
}

Is it possible to use variable b to be the dynamic variable to call a method from service?

Stackblitz example

Updated snippet based on the example, I could do the following kind:

if (this.title == 'Students') {
  this.studentService.getStudents().then(results=> this.lists = results);
} else {
  this.studentService.getCandidates().then(results => this.lists = results);
}

But is it possible to do something like:

this.studentService.get[this.title]().then(results=> this.lists = results);

as the duplicate said it won't work that way.

Shinjo
  • 677
  • 6
  • 22
  • 2
    `service.b` should be `service[b]` – Rajesh May 24 '19 at 08:45
  • @Rajesh doesn't seem to be the way to access a service. I mean if I used the conventional way like `this.service.method1(filter).....` and `this.service.method2(filter)....` with the same procedure. It would do the job, but it's not DRY. – Shinjo May 24 '19 at 08:47
  • Per my understanding, you have `method1`/`method2` in service object and you need to call it but method's name is stored in `b`. If this is correct, `service[b]` will return method's reference and adding `(...)` will call it. So unless I understood wrong, it should work – Rajesh May 24 '19 at 08:50
  • Provide a more complete example, where 'method1' and 'method2' exist in the way you expect them to. Implement a basic example where 'this' and 'this.service' mean something, so we can understand how you're trying to access method1 and method2 – TKoL May 24 '19 at 08:51
  • Responding to your comment, DRY is a concept to have generic logic and not function calls. Having method name in a variable and calling through it will reduce the readability. In this example, I would prefer using proper names as thats more clear and robust – Rajesh May 24 '19 at 08:53
  • @Rajesh I've added the example. Previously I was thinking it was enough snippet to demonstrate problem but apparently, it doesn't. – Shinjo May 24 '19 at 09:11
  • @TKoL I've added the example snippet. And the updated explanation. – Shinjo May 24 '19 at 09:19
  • 1
    OK so if `this.title` can equal 'Students' or 'Candidates', and `this.studentService` has methods `getStudents` and `getCandidates`, then you can do `this.studentService[\`get${this.title}\`]()` – TKoL May 24 '19 at 09:24
  • @TKoL Can you post that as an answer? I think it would help a lot for others. I was asking this since there was none of the related article explain about this. – Shinjo May 24 '19 at 09:29
  • Unfortunately answers aren't allowed, it was marked as a duplicate and it actually looks like the question that this is a dupe of pretty directly answers your question, showing how you can use a variable string to call a function on the object. – TKoL May 24 '19 at 09:32
  • @Shinjo I still feel this is a dupe. `get[this.title]` is a syntax error. As mentioned by TKoL, you will have to create methodString properly. My suggestion is to create a `titleHandlerMap` and then do `this.studentService[ titleHandlerMap[ this.title ] ]().then(...)` – Rajesh May 24 '19 at 09:37
  • @Shinjo TKoL's answer is almost the same what Rajesh said and is therefore also answered by other questions on SO. – Martin Schneider May 24 '19 at 09:57
  • so in your Stackblitz example it is enough to use: `this.studentService['get' + this.title]().then(results => this.lists = results);` – Martin Schneider May 24 '19 at 12:16

2 Answers2

1

You can use index access to get a specific method in Typescript, the trick is the type of the index argument. If the type does not have an index signature (and classes generally should not) then the index argument has to be of type keyof

class DoStuff {
    messageA() : Promise<string> {
        return new Promise(r=> r("messsageA"));
    }

    messageB() : Promise<string> {
        return new Promise(r=> r("messsageB"));
    }
}

let k: keyof DoStuff = Math.random()> 0.5 ? "messageA" : "messageB";
let o = new DoStuff();
o[k]().then(m=> console.log(m))

If the class contains more methods with incompatible signatures you might not be able to call the result of o[k]. You can get around this either by listing the exact methods you want to index (`let k :"messageA" | "messageB"). Or you can use a type taht filters the keys:

class DoStuff {
    messageA() : Promise<string> {
        return new Promise(r=> r("messsageA"));
    }

    messageB() : Promise<string> {
        return new Promise(r=> r("messsageB"));
    }
    util(): string { return ""}
}

let k: keyof DoStuff = (Math.random()> 0.5 ?"messageA" : "messageB") as keyof DoStuff;
let o = new DoStuff();
o[k]().then(m=> console.log(m)) //error

type FilterKeysByType<T, V> = { [P in keyof T] : T[P] extends V ? P : never}[keyof T]
let kok:FilterKeysByType<DoStuff, ()=> Promise<string>>  =  (Math.random()> 0.5 ?"messageA" : "messageB") as FilterKeysByType<DoStuff, ()=> Promise<string>>;
o[kok]().then(m=> console.log(m)) //error
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
-1

Yes, but instead of making it a string, you can assign the function to the variable, e.g.

function bla() {
  console.log('bla');
}
let b = bla;
b();

Since I am seeing some downvotes, I should elaborate on this. If you want to call b on this.service, you cannot do it as described above.

Functions on an object are stored under the key with the function name. You should use strings there. For example

let thing = {};
thing.a = function() {}
let b = 'a';
thing[b]();

If your object is a class, same thing applies, e.g.

class Thing {
 bla() {}
}
let a = new Thing();
let b = "bla";
a[b]();
minitauros
  • 1,920
  • 18
  • 20