Skip to content

Instantly share code, notes, and snippets.

@bhalash
Last active September 16, 2024 20:11
Show Gist options
  • Save bhalash/926a1a9f2ac72c43c3a2fed894994eef to your computer and use it in GitHub Desktop.
Save bhalash/926a1a9f2ac72c43c3a2fed894994eef to your computer and use it in GitHub Desktop.
homebrew recreation of util.promisify
/**
* My original mistake was trying to pluck the argument and return types using
* the Parameters<Fn> and Arguments<Fn> generic helpers. It...got messy.
*
* The method overrides act like a switch statement for type. When given a
* function as an argument, Typescript looks through the overrides to see which
* one matches.
*
* Take this function, which takes a callback and prints out the given name
* through it:
*
* @example
*
* export type Callback<T = void> = (error: any, result: T) => void;
*
* export function printNameCallback(name: string, callback: Callback<string>): void {
* callback(null, name);
* }
*
* When I pass this to toPromise, it looks through the overrides until it finds
* the version that fits the function signature:
*
* @example
*
* export function toPromise<A1, TResult>(
* fn: (arg1: A1, callback: Callback<TResult>) => void
* ): (arg1: A1) => Promise<TResult>;
*
* // printNamePromise(arg1: string): Promise<string>
* const printNamePromise = toPromise(printNameCallback);
*
* From this, the Typescript compiler can infer the correct number and type of
* arguments for the promisified function.
*/
export type Callback<T = unknown> = (error: any, result: T) => void;
export function printNameCallback(name: string, callback: Callback<string>): void {
callback(null, name);
}
export function printSum(a: number, b: number, callback: Callback<number>): void {
callback(null, a + b);
}
export function toPromise(fn: (callback: Callback) => void): () => Promise<void>;
export function toPromise<TResult>(fn: (callback: Callback<TResult>) => void): () => Promise<TResult>;
export function toPromise<A1, TResult>(fn: (arg1: A1, callback: Callback<TResult>) => void): (arg1: A1) => Promise<TResult>;
export function toPromise<A1, A2, TResult>(fn: (arg1: A1, arg2: A2, callback: Callback<TResult>) => void): (arg1: A1, arg2: A2) => Promise<TResult>;
export function toPromise<A1, A2, A3, TResult>(fn: (arg1: A1, arg2: A2, arg3: A3, callback: Callback<TResult>) => void): (arg1: A1, arg2: A2, arg3: A3) => Promise<TResult>;
export function toPromise<T>(fn: Function) {
if (typeof fn !== 'function') {
throw new TypeError('Passed argument is not a function.');
}
return (...args: any[]): Promise<T> => {
return new Promise((resolve, reject) => {
return fn(...args, (error: any, result: any) => {
if (error) {
return reject(error);
} else {
return resolve(result);
}
});
});
};
}
printNameCallback('Garrett', (_err, name) => console.log(name));
toPromise(printNameCallback)('Garrett').then(console.log);
const printNamePromise = toPromise(printNameCallback);
printNamePromise('Blah');
const promiseSum = toPromise(printSum);
promiseSum(3, 7).then(console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment