0

I avoid throwing errors in my code as they are hard to reason about. I prefer to do this:

type CanError<E> = E | undefined

function foo():CanError<Error> {
 ... whatever
 if(bad) return new Error('did error') // error path
 return undefined // success
}

const bar = foo()
if(bar!==undefined) doErrorPath(bar)
doNormalPath()

However, the risk of this approach is that I forget to handle the error path, causing weird bugs.

Is there a way one can use the Typescript compiler to force one to always handle any function that returns a value other than void as follows:

const bar = foo()

As foo returns a value other than void the following should not be allowed:

foo()

Whilst this doesn't completely guarantee I've appropriately handled the error path, it does mean I can identify places where I for sure haven't handled the error path.

TrevTheDev
  • 2,616
  • 2
  • 18
  • 36
  • I wouldn't consider this as a good design though. The return type of a function gives you an idea about what the function produces. When the function encounter error and cannot produce the expected result, it should throw. For example, it makes sense that `divide(x:number):number` normally return a number and throw when `x == 0`. Changing it to `divide(x:number):number|Error` makes it unnecessarily complicated. Why would you need to determine whether the return value is a number or an Error when you are just plugging in a normal value? Error is meant to be thrown. – Ricky Mo May 31 '23 at 05:36
  • Throwing error in fact makes your code easier to reason about. It is like a second return path, but dedicated for error. You don't need to do extra if check to figure out if the return value is an Error or a normal value. try-catch naturally do it for you. Mixing error with normal value is what truly make it harder to reason about. Always throws error when you can, only silent it at the outermost layer when you really need to (e.g. when it comes to UI rendering/ writing final result into file/ sending response to an API request etc.) – Ricky Mo May 31 '23 at 05:50
  • @Ricky Mo - the problem with that approach becomes apparent in an async environment where it can become a nightmare to find what is actually causing the code to throw. Forcing oneself to never throw and always handle every possible output avoids that nightmare altogether. – TrevTheDev Jun 06 '23 at 02:40
  • As a good practice, one should always expect an async function to throw. That's why [Unhandled promise rejection warning](https://stackoverflow.com/questions/40500490/what-is-an-unhandled-promise-rejection) exists. – Ricky Mo Jun 06 '23 at 02:43
  • @Ricky Mo my approach achieves the same, but in a better way. Try catch is arguably less clear - as it catches any and all errors - including one's one may not know anything about. My approach avoids that as any throw is by default not permitted. And all outputs must be handled. – TrevTheDev Jun 06 '23 at 02:49
  • No it is not. You did not "prohibit" throwing any error. If there happened to be other error you didn't expected (e.g. some runtime error in the internal logic), it will still throw and crash your app since you did not catch it. It is better to catch all errors, distinguish the type of error, handle the cases you know about, and leave a last default path for any error you don't know about (log it, send to server, display an alert etc.) to handle any possible error gracefully. Unless you really need your app to crash on exception (I admit there are legit use case for this). – Ricky Mo Jun 06 '23 at 02:57
  • @RickyMo my approach forces one to deal with all possible outputs from a function (including known errors). It is a great approach as it eliminates unhandled known errors. Also as error handling is always by the calling function - it's much easier to determine the source of any errors. Both approaches are similar, except Try Catch is like `goto` - which can work, but in an async environment, it can also quickly become a nightmare of spaghetti code. Try-catch should be limited to catching genuine unknown and unforeseen errors, and doing things like exiting gracefully. – TrevTheDev Jun 07 '23 at 00:51

0 Answers0