Skip to content

Commit

Permalink
feat: promisify utility now preserves typing info
Browse files Browse the repository at this point in the history
  • Loading branch information
emmacasolin committed Jun 3, 2022
1 parent 3bbca0d commit 9f4bda2
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/bin/polykey-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const logger = new Logger('polykey', undefined, [new StreamHandler()]);
*/
async function main(_argv = process.argv): Promise<number> {
const exitHandlers = new binUtils.ExitHandlers();
const processSend = promisify<void>(process.send!.bind(process));
const processSend = promisify(process.send!.bind(process));
const { p: messageInP, resolveP: resolveMessageInP } =
promise<AgentChildProcessInput>();
process.once('message', (data: AgentChildProcessInput) => {
Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ type POJO = { [key: string]: any };
type Opaque<K, T> = T & { readonly [brand]: K };
declare const brand: unique symbol;

/**
* Generic callback
*/
type Callback<P extends Array<any> = [], R = any, E extends Error = Error> = {
(e: E, ...params: Partial<P>): R;
(e?: null | undefined, ...params: P): R;
};

/**
* Non-empty array
*/
Expand Down Expand Up @@ -81,6 +89,7 @@ type FileHandle = fs.promises.FileHandle;
export type {
POJO,
Opaque,
Callback,
NonEmptyArray,
AbstractConstructorParameters,
Initial,
Expand Down
28 changes: 22 additions & 6 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FileSystem, Timer } from '../types';
import type { FileSystem, Timer, Callback } from '../types';
import os from 'os';
import process from 'process';
import path from 'path';
Expand Down Expand Up @@ -138,18 +138,34 @@ async function poll<T, E = any>(

/**
* Convert callback-style to promise-style
* If this is applied to overloaded function
* it will only choose one of the function signatures to use
*/
function promisify<T>(f): (...args: any[]) => Promise<T> {
return function <T>(...args): Promise<T> {
function promisify<
T extends Array<unknown>,
P extends Array<unknown>,
R extends T extends [] ? void : T extends [unknown] ? T[0] : T,
>(
f: (...args: [...params: P, callback: Callback<T>]) => unknown,
): (...params: P) => Promise<R> {
// Uses a regular function so that `this` can be bound
return function (...params: P): Promise<R> {
return new Promise((resolve, reject) => {
const callback = (error, ...values) => {
if (error != null) {
return reject(error);
}
return resolve(values.length === 1 ? values[0] : values);
if (values.length === 0) {
(resolve as () => void)();
} else if (values.length === 1) {
resolve(values[0] as R);
} else {
resolve(values as R);
}
return;
};
args.push(callback);
f.apply(this, args);
params.push(callback);
f.apply(this, params);
});
};
}
Expand Down

0 comments on commit 9f4bda2

Please sign in to comment.