Basically I am trying to create a function defined like this:
function result<T>(cb: () => T): Result<T,Error> {
try {
return Ok(fn());
} catch (error) {
return Err(
error instanceof Error ? (error as E) : (new Error(String(error)) as E)
);
}
}
it is a function that takes in another function that could throw e.g JSON.parse
, and returns a rust-like result accordingly, so that when e.g result(() => JSON.parse(...))
fails it doesnt crash the program but it returns a Result with the error.
Now, this function works fine, but the problems arrive when i try to do async stuff.
i have this async variant:
async function resultrAsync<V, E extends Error>(
fn: () => Promise<V>
): Promise<Result<V, E>> {
try {
return Ok(await fn());
} catch (error) {
return Err(
error instanceof Error ? (error as E) : (new Error(String(error)) as E)
);
}
}
that should be used like this:
const result = await resultAsync(() => fetch(...))
and it works fine.
But i want to overcomplicate things and join these 2 functions and do some generic magic, to handle cases where the callback functions is synchronous or not (JSON.parse
vs fetch
)
I tried with this:
type SyncOrAsync<V> = V | Promise<V>;
function resultr<V, E extends Error>(fn: () => V): Result<V, E>;
function resultr<V, E extends Error>(
fn: () => Promise<V>
): Promise<Result<V, E>>;
function resultr<V, E extends Error>(
fn: () => SyncOrAsync<V>
): SyncOrAsync<Result<V, E>> {
try {
const result = fn();
if (result instanceof Promise) {
return result
.then(Ok)
.catch(error =>
Err(
error instanceof Error
? (error as E)
: (new Error(String(error)) as E)
)
) as Promise<Result<V, E>>;
} else {
return Ok(result);
}
} catch (error) {
return Err(
error instanceof Error ? (error as E) : (new Error(String(error)) as E)
);
}
}
But when i use the async variant const res = result(() => fetch(url))
its return type is Result<Promise<Response>, Error>
when it should be Promise<Result<V,Error>>
is this possible? i know its not probably a good idea but i want to get a grasp on generics.