Last active
August 20, 2021 17:31
-
-
Save AlexRex/e0a7aaa9246ad35707d4967f28156939 to your computer and use it in GitHub Desktop.
Result type helpers
This file contains 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
import { ApplicationErrorCodes } from './application-error-codes' | |
export class ApplicationError extends Error { | |
constructor( | |
public readonly errorCode: string, | |
description?: string | |
) { | |
super(description || errorCode) | |
// Need to override the prototype | |
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work | |
Object.setPrototypeOf(this, ApplicationError.prototype) | |
} | |
} | |
export const fromError = (error: Error) => new ApplicationError(ApplicationErrorCodes.INTERNAL_ERROR, error.message) | |
export type Ok<T> = { | |
value: T | |
} | |
export const ok = <T>(data: T): Ok<T> => ({ | |
value: data | |
}) | |
export type Result<T, E extends ApplicationError = ApplicationError> = Ok<T> | E | |
export type PromiseResult<T, E extends ApplicationError = ApplicationError> = Promise<Result<T, E>> | |
/** | |
* Expects a function that may throw. If it throws it returns the error passed | |
*/ | |
export const fromAsyncTryCatch = <O extends Error = Error, E extends ApplicationError = ApplicationError>(errorFn: (originalError: O) => E) => | |
async <DataType>(fn: () => Promise<DataType>): PromiseResult<DataType, E> => { | |
try { | |
const res = await fn() | |
return ok<DataType>(res) | |
} catch (catchErr) { | |
return errorFn(catchErr) | |
} | |
} | |
export const fromTryCatch = <O extends Error = Error, E extends ApplicationError = ApplicationError>(errorFn: (originalError: O) => E) => | |
<DataType>(fn: () => DataType): Result<DataType, E> => { | |
try { | |
const res = fn() | |
return ok<DataType>(res) | |
} catch (catchErr) { | |
return errorFn(catchErr) | |
} | |
} | |
export const effectiveError = fromAsyncTryCatch<Error | ApplicationError, ApplicationError>( | |
(error) => { | |
if (error instanceof ApplicationError) { | |
return error | |
} | |
return fromError(error) | |
} | |
) | |
export const isErrorResult = <T = unknown, E extends ApplicationError = ApplicationError>(result: Result<T, E>): result is E => | |
'errorCode' in result | |
export const isOkResult = <T = unknown, E extends ApplicationError = ApplicationError>(result: Result<T, E>): result is Ok<T> => | |
'value' in result | |
export const filterOkResults = <T = unknown, E extends ApplicationError = ApplicationError>(results: Array<Result<T, E>>) => | |
results.filter((result): result is Ok<T> => isOkResult(result)) | |
export const filterErrorResults = <T = unknown, E extends ApplicationError = ApplicationError>(results: Array<Result<T, E>>) => | |
results.filter((result): result is E => isErrorResult(result)) | |
export const extractOkResults = <T = unknown>(oks: Array<Ok<T>>) => oks.map(({ value }) => value) | |
export const isOkResults = <T = unknown, E extends ApplicationError = ApplicationError>(results: Array<Result<T, E>>) => | |
results.every((result): result is Ok<T> => isOkResult(result)) | |
/** | |
* Example function | |
*/ | |
const returningResult = (num: number): PromiseResult<number> => | |
effectiveError(async () => { | |
if (num === 0) { | |
throw new Error('hack') // effectiveError will transform it into an application error | |
} | |
return 1 | |
}) | |
const usingFn = async () => { | |
const result = await returningResult(0) | |
if (isErrorResult(result)) { | |
return console.log(result) // Application error | |
} | |
return result.value + 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment