简体   繁体   English

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

[英]Passing class method as parameter in Typescript

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.但这涉及我不想要的静态函数。

This doesn't compile-time check that the function came from a Foo , but does the rest:这不会在编译时检查函数是否来自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

Typescript 2+ solution打字稿 2+ 解决方案

TL;DR : TypeScript Playground , Repo with a demo TL;DR : TypeScript Playground , Repo with a demo

Advantages:优点:

  1. Compile-time checking.编译时检查。
  2. Won't let you lost this context when passing an instance's method.传递实例的方法时不会让您丢失this上下文。
  3. Don't lose performance: don't have to declare class' methods as instance methods (eg public somefunc = () => { return this.prop; } ) - Learn more .不要失去性能:不必将类的方法声明为实例方法(例如public somefunc = () => { return this.prop; } ) - 了解更多
  4. Don't mess with a class's prototype.不要弄乱类的原型。
  5. Consistent signature pattern: passing a callback as the first arg and thisArg as the second (eg Array.prototype.map() ).一致的签名模式:将回调作为第一个参数传递,将thisArg作为第二个参数传递(例如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 :将注意力转向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.这是解决方案的第一部分,不会让您丢失this上下文。

The second part is bar function overloads:第二部分是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

This would let you you pass generic functions as well as instance methods.这将使您可以传递通用函数以及实例方法。

You can learn more about these topics in TypeScript Handbook:您可以在 TypeScript 手册中了解有关这些主题的更多信息:

  1. this parameters in callbacks 回调中的this参数
  2. Function overloads 函数重载
  3. Generics 泛型

I'm assuming you're looking for some way for the TypeScript compiler to enforce that the given function exists on Foo?我假设您正在寻找某种方式让 TypeScript 编译器强制执行给定的函数存在于 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:也许另一位 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

Yes, declare function like this:是的,像这样声明函数:

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

Call it like this from typescript:从打字稿中这样称呼它:

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

Or from javascript:或者从javascript:

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

Also you can pass method:你也可以通过方法:

myfunction(this.someMethod);

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 !希望能帮助到你 !

You could use fat arrow functions.您可以使用粗箭头函数。 They shouldn't lose "this"他们不应该失去“这个”

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

Javascript would allow this, but not sure if that is what you want? Javascript 会允许这样做,但不确定这是否是您想要的?

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

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

console.log(bar("new Foo().someFunc('erik')"));

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.也许这不是你所要求的,但它应该是这样的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM