web-dev-qa-db-fra.com

Passer la méthode de classe comme paramètre dans Typescript

Je cherche une possibilité de passer une méthode de classe à une fonction qui peut ensuite exécuter cette fonction sur une instance de cette classe. Quelque chose comme ce pseudocode: (notez que c'est un exemple abstrait)

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

Y a-t-il une possibilité de le faire?

Je sais que je pourrais faire quelque chose comme ça:

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

mais cela implique des fonctions statiques dont je ne veux pas.

25
xDreamCoding

Cela ne vérifie pas à la compilation que la fonction provenait d'un Foo, mais fait le reste:

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
18
Ryan Cavanaugh

Solution TypeScript 2+

TL; DR : TypeScript Playground , Repo avec une démo

Avantages:

  1. Vérification à la compilation.
  2. Ne vous laissera pas perdre le contexte this lors du passage de la méthode d'une instance.
  3. Ne perdez pas les performances: vous n'avez pas à déclarer les méthodes de classe comme méthodes d'instance (par exemple, public somefunc = () => { return this.prop; }) - En savoir plus .
  4. Ne jouez pas avec le prototype d'une classe.
  5. Modèle de signature cohérent: passage d'un rappel comme premier argument et thisArg comme deuxième (par exemple Array.prototype.map () ).

Considérez le code suivant:

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

Portez votre attention sur la signature de Foo#func:

public func(this: Foo): number

Il indique que cette fonction doit être invoquée dans un contexte d'instance de classe. C'est la première partie de la solution qui ne vous laissera pas perdre le contexte this.

La deuxième partie est bar surcharge de fonction:

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

Cela vous permettrait de passer des fonctions génériques ainsi que des méthodes d'instance.

Vous pouvez en savoir plus sur ces sujets dans le manuel TypeScript:

  1. this paramètres dans les rappels
  2. surcharge de fonction
  3. Génériques
9
Deilan

Je suppose que vous cherchez un moyen pour le compilateur TypeScript de faire en sorte que la fonction donnée existe sur Foo? Malheureusement, je ne pense pas qu'il y ait moyen de le faire. Peut-être qu'un autre gourou de TypeScript peut venir ici et y répondre plus concrètement, mais je suis presque sûr que c'est le plus proche que vous pouvez obtenir:

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
3
thetoast

Oui, déclarez une fonction comme celle-ci:

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

Appelez-le comme ceci à partir de TypeScript:

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

Ou depuis javascript:

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

Vous pouvez également passer la méthode:

myfunction(this.someMethod);
1
devi

Javascript permettrait cela, mais vous ne savez pas si c'est ce que vous voulez?

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

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

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