Skip to content

Commit

Permalink
feat(maybify): add maybify
Browse files Browse the repository at this point in the history
Add maybify
  • Loading branch information
Marviel committed Nov 10, 2023
1 parent 6b0a563 commit 05ad1d8
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/functions/maybify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
failureMaybe,
IfMaybe,
isFailureMaybe,
isSuccessMaybe,
Maybe,
successMaybe,
} from './types';

/**
* Converts a function that may throw into a function that returns a maybe object.
* @param fn
* @returns
*/
export function maybify<TIn extends any[], TOut>(fn: (...input: TIn) => Promise<TOut>): (...input: TIn) => Promise<IfMaybe<TOut, TOut, Maybe<TOut>>> {
// TODO: this should gracefully handle Synchronous functions as well.
//@ts-ignore
return async (...input: TIn) => {
try {
const result = await fn(...input);

// If this is already a maybe, return it as-is.
if (isSuccessMaybe(result) || isFailureMaybe(result)) {
return result;
}
// If this wasn't a maybe, return a success maybe.
else {
return successMaybe(result);
}
} catch (error: any) {
// If we encounter any errors, return a failure maybe.
return failureMaybe(error);
}
};
}
44 changes: 44 additions & 0 deletions src/functions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,39 @@ export type Maybe<T> =
| { success: true; data: T; error?: undefined }
| { success: false; error: Error; data?: undefined };


/**
* Helper to create a success maybe object.
* @param obj The data object.
* @returns A success maybe object.
*/
export function successMaybe<T>(data: T): Maybe<T> {
return { success: true, data, error: undefined };
}

/**
* Helper to create a failure maybe object.
* @param error The error object.
* @returns A failure maybe object.
*/
export function failureMaybe(error: Error): Maybe<any> {
return { success: false, error, data: undefined };
}

export function isSuccessMaybe<T>(item: any): item is { success: true; data: T } {
// Make sure we have all the necessary fields, and that success is true.
return (item.success !== undefined && item.data !== undefined) && item.success;
}

export function isFailureMaybe<T>(item: any): item is { success: false; error: Error } {
// Make sure we have all the necessary fields, and that success is false.
return (item.success !== undefined && item.error !== undefined) && !item.success;
}

export type IfMaybe<T, Y, N> = T extends Maybe<any> ? Y : N;



/**
* A utility type for defining async functions with given input and output types.
*
Expand Down Expand Up @@ -168,3 +201,14 @@ export type RequiredConstructorConfigType<
T,
Prop extends keyof ConstructorParameters<T>[0]
> = NonNullable<ConstructorParameters<T>[0][Prop]>;




/**
* Determine if an array is not empty, meaning it has at least one element.
*/
//@ts-ignore
function notEmptyArr<T>(arr: T[]): arr is [T, ...T] {
return arr.length >= 1;
}
29 changes: 29 additions & 0 deletions test/maybify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import _ from 'lodash';

import { maybify } from '../src/functions/maybify';
import { Maybe } from '../src/functions/types';

describe('maybify', () => {
it('Should handle an already-maybed function correctly.', (): any => {
const fn = async (input: number): Promise<Maybe<number>> => {
return {
success: true,
data: input + 1
}
}

const result = maybify(fn)(5);

expect(result).resolves.toEqual({ success: true, data: 6, error: undefined });
})

it('Should handle a function that returns a value correctly.', (): any => {
const fn = async (input: number) => {
return input + 1;
};

const result = maybify(fn)(5);

expect(result).resolves.toEqual({ success: true, data: 6, error: undefined });
})
})

0 comments on commit 05ad1d8

Please sign in to comment.