Last active
July 4, 2023 09:40
-
-
Save melbourne2991/c87cebb39ad45f19717bd2acf1c3c6e6 to your computer and use it in GitHub Desktop.
Typescript utils for safe code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type ILeft<L> = { | |
tag: 'left'; | |
value: L; | |
}; | |
export type IRight<R> = { | |
tag: 'right'; | |
value: R; | |
}; | |
export type IEither<L, R> = ILeft<L> | IRight<R>; | |
export const Either = { | |
Left<L>(value: L): ILeft<L> { | |
return { tag: 'left', value }; | |
}, | |
Right<R>(value: R): IRight<R> { | |
return { tag: 'right', value }; | |
}, | |
isLeft<L, R>(either: IEither<L, R>): either is ILeft<L> { | |
return either.tag === 'left'; | |
}, | |
isRight<L, R>(either: IEither<L, R>): either is IRight<R> { | |
return either.tag === 'right'; | |
}, | |
map<L, A, B>(either: IEither<L, A>, f: (a: A) => B): IEither<L, B> { | |
if (Either.isRight(either)) { | |
return Either.Right(f(either.value)); | |
} else { | |
return either; // propagate the error | |
} | |
}, | |
flatMap<L, A, B>( | |
either: IEither<L, A>, | |
f: (a: A) => IEither<L, B> | |
): IEither<L, B> { | |
if (Either.isRight(either)) { | |
return f(either.value); | |
} else { | |
return either; // propagate the error | |
} | |
}, | |
}; | |
export type IError<E> = ILeft<E>; | |
export type ISuccess<S> = IRight<S>; | |
export type IResult<E, S> = IEither<E, S>; | |
export type ExtractLeft<EitherType> = EitherType extends ILeft<infer L> | |
? L | |
: never; | |
export type ExtractRight<EitherType> = EitherType extends IRight<infer R> | |
? R | |
: never; | |
export type ExtractError<ResultType> = ResultType extends IError<infer E> | |
? E | |
: never; | |
export type ExtractSuccess<ResultType> = ResultType extends ISuccess<infer S> | |
? S | |
: never; | |
export const Result = { | |
Error: Either.Left, | |
Success: Either.Right, | |
isError: Either.isLeft, | |
isSuccess: Either.isRight, | |
map<E, A, B>(result: IResult<E, A>, f: (a: A) => B): IResult<E, B> { | |
return Either.map(result, f); | |
}, | |
flatMap<E, A, B>( | |
result: IResult<E, A>, | |
f: (a: A) => IResult<E, B> | |
): IResult<E, B> { | |
return Either.flatMap(result, f); | |
}, | |
}; | |
// Create a type for a function that might throw an error | |
export type MayThrow<T, A extends any[]> = (...args: A) => T; | |
// This is a utility function that wraps a potentially error-throwing function | |
export function safeCall<T, A extends any[]>( | |
f: MayThrow<T, A>, | |
...args: A | |
): IResult<Error, T> { | |
try { | |
const result = f(...args); | |
return Result.Success(result); | |
} catch (e) { | |
return Result.Error(e instanceof Error ? e : new Error(String(e))); | |
} | |
} | |
// Create a type for a function that might throw an error | |
export type MayThrowAsync<T, A extends any[]> = (...args: A) => Promise<T>; | |
// This is a utility function that wraps a potentially error-throwing function | |
export async function safeCallAsync<T, A extends any[]>( | |
f: MayThrowAsync<T, A>, | |
...args: A | |
): Promise<IResult<Error, T>> { | |
try { | |
const result = await f(...args); | |
return Result.Success(result); | |
} catch (e) { | |
return Result.Error(e instanceof Error ? e : new Error(String(e))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment