2017-10-03 5 views
1

Kontext

Kürzlich arbeitete ich an "Promistification" einer Drittanbieter-Bibliothek. Im Grunde ist die Bibliothek voll von NodeJS-asynchronen Stilfunktionen (die Callback als letztes Argument verwenden). I.e. die Funktionen, die Signaturen ähnlich wie diese haben:Typescript Typ Inferenz in einer verallgemeinerten "promisify" -Funktion

function foo(arg1: string, arg2: number, ..., callback: (error, result) => void): void

Ich habe versucht, eine Funktion schreiben würde den Code zum Einwickeln der ursprünglichen Funktionen und machen sie in Promise<T> Rückkehr diejenigen reduzieren:

function cb<TResult>(
    resolve: (res: TResult) => void, 
    reject: (err: any) => void 
): (actualError, actualResult) => void { 

    return (error, result) => error ? reject(error) : resolve(result); 
} 

Dann die Methoden promisify, würde ich Code wie das schreiben:

patchUserMetadata(userId: string, userMetadata: any): Promise<a0.Auth0UserProfile> { 
    return new Promise((resolve, reject) => 
    this.wrapped.patchUserMetadata(userId, userMetadata, cb(resolve, reject))); 
} 

linkUser(userId: string, secondaryUserToken: string): Promise<any> { 
    return new Promise((resolve, reject) => 
    this.wrapped.linkUser(userId, secondaryUserToken, cb(resolve, reject))); 
} 

// ... and so on, and on, and on... 

Wie Sie leicht sehen können, bin ich mit TypeScript immer noch nicht vertraut und versuchte im Grunde, ein Rad neu zu erfinden. Mein Rad endete als ein Sechseck und ich hielt zu viel Verpackung Code von Hand zu schreiben ...

Jemand, der meinen Code überprüft darauf hingewiesen, dass ich ähnliches Ergebnis js-promisify zu erzielen bei geringeren Kosten nutzen können. Die Bibliothek definiert einen Helfer, der die Arbeit erledigt:

module.exports = function (fun, args, self) { 
    return new Promise(function (resolve, reject) { 
    args.push(function (err, data) { 
     err && reject(err); 
     resolve(data); 
    }) 
    fun.apply(self, args); 
    }); 
}; 

Da ich mit Typoskript anstatt JavaScript zu tun habe, ging ich weiter und tat ein wenig Forschung. Dies ist, wie ich typed-promisify Kommissionierung endete und der Code sah nun wie folgt aus:

patchUserMetadata = promisify(this.wrapped.patchUserMetadata); 

linkUser = promisify(this.wrapped.linkUser); 

viel ordentlicher, nicht wahr?

Näher

Ich habe mich gefragt, wie genau funktioniert diese Funktion promisify Arbeit? Ich schaute auf den Quellcode und eine Lösung gefunden, die zu js-promisify ‚s ein ähnlich funktioniert:

export function promisify<T>(f: (cb: (err: any, res: T) => void) => void, thisContext?: any):() => Promise<T>; 
export function promisify<A, T>(f: (arg: A, cb: (err: any, res: T) => void) => void, thisContext?: any): (arg: A) => Promise<T>; 
export function promisify<A, A2, T>(f: (arg: A, arg2: A2, cb: (err: any, res: T) => void) => void, thisContext?: any): (arg: A, arg2: A2) => Promise<T>; 
// ...more overloads 

export function promisify(f: any, thisContext?: any) { 
    return function() { 
    let args = Array.prototype.slice.call(arguments); 
    return new Promise((resolve, reject) => { 
     args.push((err: any, result: any) => err !== null ? reject(err) : resolve(result)); 
     f.apply(thisContext, args); 
    }); 
    } 
} 

Frage

Wenn man sich die promisify genau hinsehen, können Sie sehen, dass diese Lösung nicht wirklich verallgemeinert. Das heißt, wenn ich eine Funktion mit 10 oder mehr Parametern promiziieren müsste, würde es keine passende Überladung geben. Die Implementierung würde immer noch funktionieren, jedoch würde die Typinformation in diesem Fall verloren gehen.

Gibt es eine Möglichkeit in TypeScript, den genauen Funktionstyp (oder Signatur oder Anzahl und Arten von Parametern) abzuleiten, ohne all diese unangenehmen Überlastungen im Voraus zu definieren?

Ich bin für so etwas suchen [offensichtlich, Pseudo-Code]:

export function promisify<...[TArgs], T>(
    f: (...allArgsButLastTwo: [TArgs], 
    cb: (err: any, res: T) => void) => void, 
    thisContext?: any 
): (...[TArgs]) => Promise<T>; 

export function promisify(
    ...allArgsButLastTwo: any[], 
    f: any, 
    thisContext?: any 
) { 
    return function() { 
    let args = Array.prototype.slice.call(arguments); 
    return new Promise((resolve, reject) => { 
     args.push((err: any, result: any) => err !== null ? reject(err) : resolve(result)); 
     f.apply(thisContext, args); 
    }); 
    } 
} 

Ich habe das Gefühl, dass das, was ich suche nicht erreichbar ist und deshalb die Liste lange Überlast eine war letzter Ausweg/Kompromisslösung, die der Autor verwenden musste.

Antwort

3

Ab Version 2.5 gibt es derzeit keine Möglichkeit, dies in Typoskript zu tun, bis das Problem gelöst wird: https://github.com/Microsoft/TypeScript/issues/5453

Es wurde auf die roadmap für eine Weile unter Variadische Typen.

+0

Vielen Dank für die Bezugnahme auf die TS-Problem und die Roadmap Filipe !! –