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;
};
AnyFunctionwird benötigt, damit wir den Rest-Operator verwenden und Parametertypen/den Rückgabewerttyp herausziehen können- Die Verwendung von
Func extends AnyFuncermöglicht es uns dann, den Typ jeder gewünschten Funktion spezifisch zu beschreiben
- Die Verwendung von
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...argszu verwenden
- Die Lösung für das spezielle
Parameters<Func> erhält beliebige Parameter als Tupel Symbol.iterator-Problem besteht speziell darin, ...Array.from(args) anstelle von ...args zu verwendenReturnType<Func> erhält einen beliebigen RückgabetypDie 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.
- Geben Sie den Rückgabewert der Wrapper-Funktion als das
T-Generikum an, das Sie umschließen. - 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].
