Reputation: 1694
I'm searching for a possibility to pass a class-method to a function which then can execute that function on an instance of that class. Something like that pseudocode: (note that this is an abstract example)
class Foo {
public somefunc() {
// do some
}
public anyfunc() {
// do any
}
}
function bar(obj: Foo ,func: "Foo.method") { // "that's what im looking for"
obj.func();
}
bar(new Foo(), Foo.somefunc); // do some
bar(new Foo(), Foo.anyfunc); // do any
Is there a possiblity to do this?
I know i could be doing something like that:
class Foo {
static somefunc(fooObj: Foo) {
// do some
}
static anyfunc(fooObj: Foo) {
// do any
}
}
interface func {
(fooObj: Foo);
}
function bar(obj: Foo, fn: func) {
fn(obj);
}
bar(new Foo(), Foo.somefunc); // do some
bar(new Foo(), Foo.anyfunc); // do any
but that involves static functions which I don't want.
Upvotes: 48
Views: 71617
Reputation: 2637
It depends on the specifics of your particular use case, but this is another option that covers a similar set of situations.
The question was about calling multiple methods of an object from a function without losing the this
pointer. I found this topic because I wanted to call the same method on multiple classes without losing the this
pointer. I am including my answer because other people might find this topic that have the same use case as me.
This is what worked for my use case:
class EnglishGreeting {
private readonly greeting = 'Hello ';
public greet(name: string): void {
console.log(this.greeting + name);
}
}
class FrenchGreeting {
private readonly greeting = 'Bonjour ';
public greet(name: string): void {
console.log(this.greeting + name);
}
}
function bar(greeter: { greet:(name: string) => void }) {
greeter.greet('Joe')
}
bar(new EnglishGreeting()); // Prints "Hello Joe"
bar(new FrenchGreeting()); // Prints "Bonjour Joe"
Upvotes: 0
Reputation: 399
In my opinion, you should use facade design pattern for this case.
When you create a complex library, tool or system,consisting of many functions and/or classes; It gets hard to understand and dependent. So you should implement a class which provides a simple uniform interface.
class Foo{
public somefunc() {
console.log("some")
}
public anyfunc() {
console.log("any")
}
};
class FuncFacade {
getFunc(obj: any, func_name: string) {
switch (func_name) {
case obj.somefunc.name: {
return obj.somefunc;
}
case obj.anyfunc.name: {
return obj.anyfunc;
}
default: {
throw new Error("No such func!");
}
}
}
}
let ff = new FuncFacade();
function bar(obj: Foo, func_name: string) {
ff.getFunc(obj,func_name)();
}
bar(new Foo(), Foo.prototype.anyfunc.name);
Maybe this was not what you asked for but this is the way how it should be.
Upvotes: 0
Reputation: 1148
You could use fat arrow functions. They shouldn't lose "this"
class Foo {
public somefunc = () => {
// do some
}
public anyfunc = () => {
// do any
}
}
Upvotes: 2
Reputation: 372
For my part; according to the statement of the problem, I could have done like this:
class Foo {
public constructor() {
this.welcome = this.welcome.bind(this)
}
public welcome(msg: string): void {
console.log(`hello ${msg}`)
}
}
function bar(msg: string, fn: void): void {
fn(msg)
}
const foo = new Foo()
bar('world', foo.welcome) // 'hello world'
In addition, I should point out that I was inspired by this clear explanation.
Hope it helps !
Upvotes: 5
Reputation: 4886
TL;DR: TypeScript Playground, Repo with a demo
Advantages:
this
context when
passing an instance's method.public somefunc = () => { return this.prop; }
) - Learn more.thisArg
as the second (e. g. Array.prototype.map()).Consider the following code:
class Foo {
private result: number = 42;
public func(this: Foo): number {
return this.result;
}
}
function action(): void {
console.log("Hello world!");
}
function bar(callbackFn: (this: void) => any, thisArg?: undefined): any;
function bar<T>(callbackFn: (this: T) => any, thisArg: T): any;
function bar<T, TResult>(callbackFn: (this: T) => TResult, thisArg: T): TResult {
return callbackFn.call(thisArg);
}
const foo = new Foo();
bar(action); // success
bar(foo.func); // ERROR: forgot to pass `thisArg`
bar(foo.func, foo); // success
Turn your attention to the signature of Foo#func
:
public func(this: Foo): number
It states that this function should be invoked in a context of the class' instance. This is the first part of the solution which won't let you lost this
context.
The second part is bar
function overloads:
function bar(callbackFn: (this: void) => any, thisArg?: undefined): any;
function bar<T>(callbackFn: (this: T) => any, thisArg: T): any;
function bar<T, TResult>(callbackFn: (this: T) => TResult, thisArg: T): TResult
This would let you you pass generic functions as well as instance methods.
You can learn more about these topics in TypeScript Handbook:
Upvotes: 21
Reputation: 4501
Yes, declare function like this:
myfunction(action: () => void){
action();
}
Call it like this from typescript:
myfunction(() => alert("hello"));
Or from javascript:
myfunction(function() { alert("hello"); });
Also you can pass method:
myfunction(this.someMethod);
Upvotes: 7
Reputation: 220964
This doesn't compile-time check that the function came from a Foo
, but does the rest:
class Foo {
public somefunc() {
// do some
}
public anyfunc() {
// do any
}
}
function bar(obj: Foo ,func: () => void) {
func.call(obj);
}
bar(new Foo(), Foo.prototype.somefunc); // do some
bar(new Foo(), Foo.prototype.anyfunc); // do any
Upvotes: 35
Reputation: 773
I'm assuming you're looking for some way for the TypeScript compiler to enforce that the given function exists on Foo? Unfortunately, I don't think there's a way to do that. Maybe another TypeScript guru can come in here and answer that more concretely, but I'm pretty sure this is the closest that you can get:
class Foo {
constructor(private name:string) { }
public somefunc() {
console.log("someFunc called on", this.name);
}
public anyfunc() {
console.log("anyFunc called on", this.name);
}
}
function bar(obj: Foo, func: string) {
if (obj[func] && obj[func] instanceof Function) {
obj[func]();
} else {
throw new Error("Function '" + func + "' is not a valid function");
}
}
bar(new Foo("foo1"), "somefunc"); // output: 'somefunc called on foo1'
bar(new Foo("foo2"), "anyfunc"); // output: 'anyfunc called on foo1'
bar(new Foo("foo3"), "badFunction"); // throws: Error: Function 'badFunction' is not a valid function
Upvotes: 6
Reputation: 1247
Javascript would allow this, but not sure if that is what you want?
class Foo {
public someFunc(name:string){
return "Hello, " + name;
}
function bar(funcName: string) {
return eval(funcName);
}
console.log(bar("new Foo().someFunc('erik')"));
Upvotes: -1