Skip to content

Instantly share code, notes, and snippets.

@melbourne2991
Last active July 4, 2023 09:40
Show Gist options
  • Save melbourne2991/c87cebb39ad45f19717bd2acf1c3c6e6 to your computer and use it in GitHub Desktop.
Save melbourne2991/c87cebb39ad45f19717bd2acf1c3c6e6 to your computer and use it in GitHub Desktop.
Typescript utils for safe code
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