Reputation: 3914
I'm trying to promisify standard node fs.writeFile. I have typings for both node and bluebird which is my Promise lib of choice here:
const f = Promise.promisify(fs.writeFile)
return f(file, content); // Should be a promise
This does not work because:
[ts] Supplied parameters do not match any signature of call target. const f: (arg1: string) => Promise<{}>
So i chooses the wrong overloaded method because the call to promisify can't really know which I guess. Or maybe it's not even that, but something with the optional arguments, because these are the three overloaded writeFile:
export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
promisify has lots of overloads for # of args and looks like this:
/**
* Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.
*
* If the `nodeFunction` calls its callback with multiple success values, the fulfillment value will be an array of them.
*
* If you pass a `receiver`, the `nodeFunction` will be called as a method on the `receiver`.
*/
static promisify<T>(func: (callback: (err:any, result: T) => void) => void, receiver?: any): () => Promise<T>;
static promisify<T, A1>(func: (arg1: A1, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1) => Promise<T>;
static promisify<T, A1, A2>(func: (arg1: A1, arg2: A2, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2) => Promise<T>;
static promisify<T, A1, A2, A3>(func: (arg1: A1, arg2: A2, arg3: A3, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3) => Promise<T>;
static promisify<T, A1, A2, A3, A4>(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Promise<T>;
static promisify<T, A1, A2, A3, A4, A5>(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Promise<T>;
static promisify(nodeFunction: Function, receiver?: any): Function;
So it seems it rather the wrong promisify that is being used. Is there any cleaner way for me to achieve what I want? Currently I have to resolve to this:
const writeFile : (filename: string, data: any, callback: (err: NodeJS.ErrnoException) => void) => void = fs.writeFile
const f = Promise.promisify(writeFile)
return f(file, content);
which I feel is very ugly and verbose…
Upvotes: 13
Views: 3665
Reputation: 905
What I end up doing is specifying myself the overloading type, the main con of this method is that once the library updates I won't be notified.
Example with mixpanel:
const mixpanelClient = Mixpanel.init(mixpanelProjectApiToken, {
protocol: 'https',
});
type trackFunc = (eventName: string, properties: PropertyDict) => Promise<void>;
const track: trackFunc = promisify(mixpanelClient.track.bind(mixpanelClient));
Upvotes: 0
Reputation: 8377
you could use __promisify__
from fs.writeFile
namespace:
fs.writeFile.__promisify__(file, content)
Otherwise, you've to explicitly mention generic types to pick corresponding overload:
util
package has a promisify()
functionconst f = util.promisify<PathLike, string, void>(fs.writeFile)
f(file, content)
Upvotes: 3
Reputation: 250832
I haven't been able to set up a good simple working example, but the following type assertion would allow you to hint and what you would expect:
const f = <(file: string, content: string) => void> Promise.promisify(fs.writeFile)
You can amend <(file: string, content: string) => void>
to better describe what you expect to be available, or use the wider type <Function>
or go dynamic with <any>
.
The first option is a little more work, but will allow the return type to be inferred when you later call f
(in this case void
- but you can imagine cases where this return type will be used more vigorously).
Based on your comment, I'm guessing you'll need...
const f = <(file: string, content: string) => Promise<any>> Promise.promisify(fs.writeFile)
You could also use a union type in place of the any
in this example, if you know the two possible outcomes, i.e. Promise<any>
could be:
Promise<ErrnoException | MySuccessfulType>
This will allow a return type of Promise<ErrnoException>
or a return type of Promise<MySuccessfulType>
.
Upvotes: 7