Hello everyone!
In my larger codebase, I employ the usage of Results<T, E> and some functional programming everywhere, to improve the error handling for my users and myself.
To make use of neverthrows classes beyond the network boundary, I have some helper for serializing and hydrating result classes:
export function hydrateSerializedResult<TData, TError>(
result: SerializedResult<TData, TError>
): Result<TData, TError> {
if (result.type === RESULT_TYPES.SUCCESS) {
return ok(result.data);
}
return err(result.error);
}
export function serializeSuccess<T>(data: T): SerializedResult<T, never> {
return { data, type: RESULT_TYPES.SUCCESS };
}
export function serializeError<T>(error: T): SerializedResult<never, T> {
return { error, type: RESULT_TYPES.ERROR };
}
Errors always share the same base, (with a code) but they can differ in their properties (for example a not found error has an ID on it, to trace that later, or a validationError has a list of invalid fields on it)
When using early returns in the loader, for example
const firstResult = await doSomething()
if (firstResult.isErr()) {
return serializeError(firstResult.error)
}
const secondResult = await doSomethingElse()
if (secondResult.isErr()){
return serializeError(secondResult.error)
}
return serializeSuccess(secondResult.value)
Now typescript complains that the both error types (despite sharing the same base) are not assignable to each other when passing the loaderData in the serializeResult function.
Is there any type-level magic I can apply here to tell TS (without manually specifying a union of possible errors) that this will infact work?
Thanks in advance!