Change the Either type signature

This commit is contained in:
Michal Vanko 2022-03-16 15:30:08 +01:00
parent 8abf4923a4
commit 5f84339d77

View File

@ -7,6 +7,7 @@ tags:
- Development - Development
- Guide - Guide
--- ---
We have started a new small internal project for automating a few workflows around counting worked hours and time offs. We have started a new small internal project for automating a few workflows around counting worked hours and time offs.
## Application architecture ## Application architecture
@ -43,11 +44,11 @@ export class InternalError extends Error {
} }
} }
``` ```
Then, we can create multiple app-specific errors by extending from these two. Then, we can create multiple app-specific errors by extending from these two.
> We need to set `Object.setPrototypeOf(...)` as TypeScript [introduced a breaking change](https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work) that may cause the inheritance to now work properly. > We need to set `Object.setPrototypeOf(...)` as TypeScript [introduced a breaking change](https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work) that may cause the inheritance to now work properly.
## Handling errors ## Handling errors
I wanted to have a similar style of handling the error as the [Ramda's `tryCatch`](https://ramdajs.com/docs/#tryCatch) function. I couldn't just use Ramda's `tryCatch` as it doesn't support asynchronous functions. I've found inspiration in the [`fp-ts` `TaskEither` type](https://gcanti.github.io/fp-ts/modules/TaskEither.ts.html). I wanted to have a similar style of handling the error as the [Ramda's `tryCatch`](https://ramdajs.com/docs/#tryCatch) function. I couldn't just use Ramda's `tryCatch` as it doesn't support asynchronous functions. I've found inspiration in the [`fp-ts` `TaskEither` type](https://gcanti.github.io/fp-ts/modules/TaskEither.ts.html).
@ -59,7 +60,7 @@ I've come up with the following solution:
* Tuple of error and a result of an asynchronous task that might throw an error * Tuple of error and a result of an asynchronous task that might throw an error
*/ */
export type Either<ResultType, ErrorType extends Error> = Promise< export type Either<ResultType, ErrorType extends Error> = Promise<
[error: ErrorType | null, result?: ResultType] [error: null, result: ResultType] | [error: ErrorType, result: undefined]
> >
/** /**
@ -102,7 +103,9 @@ As most of the errors will be possibly handled by the same `errorHandle` we can
* It will throw an error if any unexpected error is thrown * It will throw an error if any unexpected error is thrown
* When used with `wrapWithErrorHandler` it is able to catch most of the expected errors * When used with `wrapWithErrorHandler` it is able to catch most of the expected errors
*/ */
export function genericErrorHandler<ErrorType extends Error>(exception: ErrorType | unknown) { export function genericErrorHandler<ErrorType extends Error>(
exception: ErrorType | unknown
) {
if (exception instanceof PublishableError) { if (exception instanceof PublishableError) {
// These errors are usually handled no need to log them // These errors are usually handled no need to log them
return exception return exception
@ -129,9 +132,10 @@ To spare some characters of redundant code I've applied a partial application pr
/** /**
* Helper function with applied `genericErrorHandler` to `wrapWithErrorHandler` * Helper function with applied `genericErrorHandler` to `wrapWithErrorHandler`
*/ */
export function wrapWithGenericErrorHandlerFunction<FunctionArgs extends Array<any>, ResultType>( export function wrapWithGenericErrorHandlerFunction<
fn: (...args: FunctionArgs) => Promise<ResultType> FunctionArgs extends Array<any>,
) { ResultType
>(fn: (...args: FunctionArgs) => Promise<ResultType>) {
return wrapWithErrorHandler(genericErrorHandler, fn) return wrapWithErrorHandler(genericErrorHandler, fn)
} }
``` ```
@ -141,7 +145,9 @@ export function wrapWithGenericErrorHandlerFunction<FunctionArgs extends Array<a
To sum it up, I'd like to present you the way how this API is used: To sum it up, I'd like to present you the way how this API is used:
```typescript ```typescript
const [error, memberId] = await wrapWithGenericErrorHandlerFunction(getUserId)(user.token) const [error, memberId] = await wrapWithGenericErrorHandlerFunction(getUserId)(
user.token
)
if (error) { if (error) {
await respond(error.message) await respond(error.message)
return return