Website-Suche

TypeScript - Ist es möglich, eine Funktion zu umschließen und ihre Typen beizubehalten?


Ich versuche, eine generische Wrapper-Funktion zu erstellen, die jede an sie übergebene Funktion umschließt.

Auf der grundlegendsten Ebene würde die Wrapper-Funktion ungefähr so aussehen

function wrap<T extends Function>(fn: T) {
    return (...args) => {
        return fn(...args)
    };
}

Ich versuche, es so zu benutzen:

function foo(a: string, b: number): [string, number] {
    return [a, b];
}

const wrappedFoo = wrap(foo);

Im Moment erhält wrappedFoo den Typ (...args: any[]) => any

Ist es möglich, wrappedFoo dazu zu bringen, die Typen der Funktion, die es umgibt, nachzuahmen?

Antwort #1

Dies funktioniert mit einer beliebigen Anzahl von Argumenten und behält alle Argumente & Rückgabewerte bei

const wrap = <T extends Array<any>, U>(fn: (...args: T) => U) => {
  return (...args: T): U => fn(...args)
}

Antwort #2

Musste das kürzlich mit Pfeilfunktionen machen, habe eine Methode entwickelt, die ein wenig einfacher funktioniert als einige der anderen Antworten, und habe ein Problem mit dem Spread-Operator und Symbol.iterator umgangen (getestet in ts 3.8)

type AnyFunction = (...args: any[]) => any;

const wrap = <Func extends AnyFunction>(
  fn: Func,
): ((...args: Parameters<Func>) => ReturnType<Func>) => {
  const wrappedFn = (...args: Parameters<Func>): ReturnType<Func> => {
      // your code here
      return fn(...args);
  };
  return wrappedFn;
};
  • AnyFunction wird benötigt, damit wir den Rest-Operator verwenden und Parametertypen/den Rückgabewerttyp herausziehen können

    • Die Verwendung von Func extends AnyFunc ermöglicht es uns dann, den Typ jeder gewünschten Funktion spezifisch zu beschreiben
  • Einfach zu sagen, dass das Ergebnis Func ist, führt zu einem Problem, weil die eingewickelte Funktion nicht mit einem anderen Subtyp instanziiert werden kann, daher müssen wir sie stattdessen neu konstruieren

    • Parameters<Func> erhält beliebige Parameter als Tupel

      • Die Lösung für das spezielle Symbol.iterator-Problem besteht darin, ...Array.from(args) anstelle von ...args zu verwenden
      • Parameters<Func> erhält beliebige Parameter als Tupel
      • Die Lösung für das Symbol.iterator-Problem besteht speziell darin, ...Array.from(args) anstelle von ...args zu verwenden

    • ReturnType<Func> erhält einen beliebigen Rückgabetyp
  • Ihre IDE sollte alle Typen korrekt für die resultierende umschlossene Funktion erkennen

Die umschlossene Funktion wird nun im Wesentlichen für alles, was Sie tun können, dieselbe Signatur wie die interne Funktion haben.

const foo = (a: string, b: number): number => a.length + b;
const wrappedFoo = wrap<typeof foo>(foo);

foo('hello', -5); // => 0
wrappedFoo('hello', -5); // => 0

Antwort #3

Es ist möglich, eine Wrapper-Funktion zu erstellen, die dieselben Typen akzeptiert und zurückgibt wie die Funktion, die sie umschließt, indem man 2 Änderungen vornimmt.

  1. Geben Sie den Rückgabewert der Wrapper-Funktion als das T-Generikum an, das Sie umschließen.
  2. Gib die Funktion, die du zurückgibst, als <any> zurück

Zum Beispiel:

function wrap<T extends Function>(fn: T): T {
    return <any>function(...args) {
        return fn(...args)
    };
}

Dann der Typ von const wrappedFoo=wrap(foo);

wird dann korrekt sein:

(a: string, b: number) => [string, number].

Verwandte Artikel