Proposing a try
operator to allow expressions to be protected directly and the result handled normally.
#61558
Closed
6 tasks done
Labels
Out of Scope
This idea sits outside of the TypeScript language design constraints
Suggestion
An idea for TypeScript
π Search Terms
try operator
try expression
safe assignment operator
safe assignment expression
try keyword
wrap an expression that throws
β Viability Checklist
β Suggestion
Have you ever wanted to catch an error from an expression without interrupting the flow of your code or resorting to cumbersome helper functions and callbacks?
If you're working with sqlite or some other synchronous data situation, you probably want to catch the errors from each specific call and call your error handler with additional information about the request that failed.
Helper functions require callbacks, which require runtime checks to be repeated inside the callback in order for the types to be properly maintained.
Using
try/catch
requires choosing between scopedconst
or unscopedvar
, and it's easy to end up with nestedtry/catch
blocks.The most common solution is to just put that one line of code in a separate function but if you need to do that for many different calls things start to get tedious, especially if they are all very similar, but not quite the same, with complicated ORM typing that needs to be preserved.
Wouldn't it be easier if we just had a try operator to inline errors and then handle them as part of our normal program?
The try operator would evaluate the expression, and if it throws, it would return the error, clearly marked, rather than throwing.
π Motivating Example
Instead of this:
or this:
we have this
or even just this
And the types are simple.
π» Use Cases
This operator seems unnecessary in the async context, where multiple alternate Promise-based solutions exist or may be easily added, but there is no equivalent in the sync world, and synchronous sqlite and fs access has the most to gain from this feature, as do generators (both sync and async).
It is rare to wrap an entire section of your own code in a try/catch block. But it's common to wrap library calls in try catch blocks to catch the random errors that specific libraries may throw. These errors may be well-defined, but they are not simple to catch and handle inline with the rest of your code.
Currently there are several solutions, but they are cumbersome or require restructuring code flow specifically because the error is possible.
The most obvious alternative would be to use an inline try helper function with an immediate callback which wraps your code in a try/catch. But that breaks runtime type checks on variables in the parent function because Typescript has no way of knowing that the callback is going to be executed immediately.
Obviously a
try/catch
statment is also possible, but that severely breaks code flow, and most developers are more likely to create a separate function to wrap that one line in atry/catch
. This quickly becomes cumbersome if there are a number of similar calls to make.In addition, the simplest solution to wrap a yield expression in a generator (in case the consumer calls
iter.throw()
) is exactlyThese are the use cases for a dedicated Try Operator that can be used inline. Unlike helper functions, this would wrap an expression in a
try
at its call site, and return a tuple that preserves type safety while avoiding redundant type checks currently required.The try operator has the same precedence as the yield operator, which will consume everything it can short of a comma. To prevent confusion with the try block, it may not be immediately followed by
{
(like the arrow function body expression).This is already being proposed for Javascript with additional technical details here, of which I am an author.
The strongest objection the authors have gotten is that browser vendors are loath to implement new syntax. Since the most compelling use-case is based on Typescript conventions, I thought perhaps it would be more compatible with the goals of Typescript.
I have already implemented this in Typescript, and it is very simple. The types work well using existing typing, so nothing had to change there. It simply transforms the try keyword into the inline version of one of these four helper functions, as demonstrated in the examples here.
The types are sufficient to allow the tuple or object destructuring usage with the correct type-narrowing (or whatever it's called). Despite the destructuring, the type information is preserved, so if one destructured parameter excludes a type in the union, the other parameters are also narrowed.
While the implementation in Javascript could be more complex, adding this to Typescript already reduces a significant amount of friction and could significantly ease the path to implementation.
This is in no way proposing any changes to how
try/catch
currently works. The type of the error parameter in the result tuple would be the same as the error parameter in a catch block, whether that's unknown or any.The text was updated successfully, but these errors were encountered: