2017-10-03 19 views
1

Contexte

Récemment, je travaillais sur « promisification » d'une bibliothèque tiers. Fondamentalement, la bibliothèque est pleine de fonctions de style asynchrones NodeJS (qui utilisent le rappel comme dernier argument). C'est à dire. les fonctions qui ont des signatures similaires à celui-ci:inférence de type dans une Tapuscrit fonction « promisify » généralisée

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

j'ai essayé d'écrire une fonction réduirait le code pour envelopper les fonctions originales et les rendre en Promise<T> les retour:

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

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

Ensuite, à promisify les méthodes, j'écrire du code comme ça:

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... 

Comme vous pouvez facilement le voir, je ne suis toujours pas très familier avec TypeScript et essayait essentiellement de réinventer une roue. Ma roue a fini par être un hexagone et je continuais à écrire trop de code d'emballage à la main ...

Quelqu'un qui a examiné mon Code a que je peux utiliser js-promisify pour obtenir des résultats similaires à moindre coût. La bibliothèque définit une aide qui fait le travail:

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); 
    }); 
}; 

Depuis que je traite tapuscrit plutôt que JavaScript, je suis allé plus loin et a fait un peu de recherche. Voilà comment je fini par choisir typed-promisify et le code maintenant regardé comme ceci:

patchUserMetadata = promisify(this.wrapped.patchUserMetadata); 

linkUser = promisify(this.wrapped.linkUser); 

beaucoup plus propres, hein?

Se rapprocher

Je me demandais comment exactement ce travail de fonction promisify? J'ai regardé le code source et trouvé une solution qui fonctionne similaire à l » un js-promisify:

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); 
    }); 
    } 
} 

Question

Si vous regardez le près promisify, vous pouvez voir que cette solution ne soit pas vraiment généralisée. Ce qui veut dire que si j'avais besoin de promettre une fonction avec plus de 10 paramètres, il n'y aurait pas de surcharge correspondante. L'implémentation fonctionnerait toujours correctement, mais les informations de type seraient perdues dans ce cas.

Existe-t-il un moyen dans TypeScript de déduire le type de fonction (ou la signature, ou le nombre et les types de paramètres) précis sans définir toutes ces surcharges désagréables à l'avance?

Je cherche quelque chose comme ça [évidemment, pseudocode]:

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); 
    }); 
    } 
} 

J'ai le sentiment que ce que je suis à la recherche est pas réalisable et c'est pourquoi une liste de surcharge était une solution de dernier recours/compromis que l'auteur devait utiliser.

Répondre

3

de la version 2.5, il n'y a actuellement aucun moyen de le faire dactylographiée jusqu'à ce que cela devient résolu: https://github.com/Microsoft/TypeScript/issues/5453

Il a été sur le roadmap pendant un certain temps, sous Types VARIADIC.

+0

Merci beaucoup pour avoir référencé le numéro TS et la feuille de route Filipe !! –