在 Typescript 中将类方法作为参数传递

问题描述 投票:0回答:9

我正在寻找将类方法传递给函数的可能性,然后该函数可以在该类的实例上执行该函数。 像这样的伪代码:(注意这是一个抽象的例子)

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

有可能这样做吗?

我知道我可以做类似的事情:

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

但这涉及我不想要的静态函数。

typescript function parameters
9个回答
35
投票

这不会在编译时检查该函数是否来自

Foo
,但会执行其余操作:

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

21
投票

Typescript 2+ 解决方案

TL;DRTypeScript Playground带演示的 Repo

优点:

  1. 编译时检查。
  2. 不会让你失去
    this
    上下文 传递实例的方法。
  3. 不要损失性能:不必将类的方法声明为实例方法(例如
    public somefunc = () => { return this.prop; }
    )-了解更多
  4. 不要弄乱类的原型。
  5. 一致的签名模式:传递回调作为第一个参数,
    thisArg
    作为第二个参数(例如Array.prototype.map())。

考虑以下代码:

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

请注意

Foo#func
的签名:

public func(this: Foo): number

它指出该函数应该在类实例的上下文中调用。这是解决方案的第一部分,它不会让您丢失

this
上下文。

第二部分是

bar
函数重载:

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

这可以让您传递泛型函数和实例方法。

您可以在 TypeScript 手册中了解有关这些主题的更多信息:

  1. this
    回调中的参数
  2. 函数重载
  3. 泛型

7
投票

是的,像这样声明函数:

myfunction(action: () => void){
   action();
}

从打字稿中这样称呼它:

myfunction(() => alert("hello"));

或者来自 javascript:

myfunction(function() { alert("hello"); });

也可以通过方法:

myfunction(this.someMethod);

6
投票

我假设您正在寻找某种方法让 TypeScript 编译器强制 Foo 上存在给定的函数? 不幸的是,我认为没有办法做到这一点。 也许另一位 TypeScript 大师可以进来并更具体地回答这个问题,但我很确定这是你能得到的最接近的答案:

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

5
投票

就我而言;根据问题的陈述,我可以这样做:

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'

此外,我应该指出,我受到了这个清晰的解释的启发。

希望有帮助!


2
投票

您可以使用粗箭头功能。他们不应该失去“这个”

class Foo {
    public somefunc = () => {
        // do some
    }
    public anyfunc = () => {
        // do any
    }
}

0
投票

在我看来,这种情况应该使用门面设计模式。

当您创建一个复杂的库、工具或系统时,由许多函数和/或类组成;它变得难以理解和依赖。所以你应该实现一个提供简单统一接口的类。

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);

也许这不是你所要求的,但这就是它应该的样子。


0
投票

这取决于您特定用例的具体情况,但这是其他答案尚未涵盖的另一个选项。

其他答案不适用于我的用例,因为我有很多使用

anyFunc
方法的类,并且我希望它可以与其中任何一个一起使用。

这对我的用例有用:

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"

-1
投票

Javascript 允许这样做,但不确定这是否是你想要的?

class Foo {
 public someFunc(name:string){
  return "Hello, " + name;
 }

function bar(funcName: string) {
    return eval(funcName);
}

console.log(bar("new Foo().someFunc('erik')"));
© www.soinside.com 2019 - 2024. All rights reserved.