I have written the following function to help with error handling
const capture = <T>(
callback: () => T
): { result?: T; error?: Error } => {
try {
return { result: callback(), error: undefined };
} catch (err) {
return { result: undefined, error: err as Error };
}
};
Example usage:
const { result, error } = capture<number>(() => foo());
if (error) badPath();
console.log("hooray we got a result", result)
However the TypeScript compiler will complain:
Object is possibly 'undefined'. ts(2532)
const result: number | undefined
I understand why the compiler is complaining (this is expected behaviour for using optional params).
However I was wondering if there existed some TypeScript shenanigans that could support conditional return types.
i.e. is there a way we could specify capture
's signature such that when error
doesn't exist, result
is inferred to exist? And vice versa?
capture
function as { result: T; error: undefined; } | { result: undefined; error: Error; }
.const capture = function<T>(
callback: () => T
): { result: T; error: undefined; } | { result: undefined; error: Error; } {
try {
return { result: callback(), error: undefined };
} catch (err) {
return { result: undefined, error: err as Error };
}
}
const retVal = capture<number>(() => foo());
if (retVal.error)
badPath();
else
console.log("hooray we got a result", retVal.result);
See this playground link.
For a more robust implementation, we need to take into account the case that the error thrown is not an instance of Error
. In this case, checking the truthness of the error
property in the result is not sufficient. We could here, for example, switch to test for the existence of the error
property itself. To do that, let's change the return type to just { result: T; } | { error: unknown; }
. An implementation would then look like:
const capture = function<T>(
callback: () => T
): { result: T; } | { error: unknown; } {
try {
return { result: callback() };
} catch (err) {
return { error: err };
}
}
const retVal = capture<number>(() => foo());
if ('error' in retVal)
badPath();
else
console.log("hooray we got a result", retVal.result);
And the playground link.
That's not possible because TypeScript doesn't know about runtime.
The closest thing that comes to mind is to allow T
to be undefined
(callback: () => T | undefined)
and then filter it along with error
, making the rest of your code understand that result
at that point is a number
and can't be undefined.
const capture = <T>(callback: () => T | undefined): { result?: T; error?: Error } => {
try {
return { result: callback(), error: undefined }
} catch (err) {
return { result: undefined, error: err as Error }
}
}
const start = () => {
const { result, error } = capture<number>(() => foo())
if (error || !result) {
// something
return
}
// result // here it will be `number`
// (cannot be undefined because we checked)
console.log('hooray we got a result', result)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With