-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Turning errors into values is dangerous and will lead to bugs. Exceptions should be either caught or propagated. #44
Comments
Its the reverse. const [error, value] = try foo(); You also won't be able to forget to handle error because value will be |
why would an exception be thrown? |
@arthurfiorette is talking about the success value. But If the correct usage is to always check the error, then why not enforce that syntactically? I think the most annoying boiler plate that could be reduced with new syntax is the separate variable declaration and assignment inside the try block. The catch block and error conditional would be roughly equivalent, so I'm not sure there's much to gain there.
Oops, though that might be a reason to not use a tuple. |
It would throw inside |
But of course |
Indeed, every possible value is legitimately throwable, including null and undefined. |
It depends on how we handle the value after the function call. For instance: const [, filename] = try config.read('outputFile');
fs.writeFileSync(
`${new Date().toISOString().slice(0, 10)}/${filename}`,
someData,
); No error is thrown, but we might end up with a very unexpected file being written. Even TS doesn't help in this case. Consider the snippet This brings us back to the well-known concern: the "safe assignment operator" (or "try-expression") can lead to implicitly ignored errors. This tradeoff must be acknowledged, and the proposal should include coverage of this issue, such as a linting rule or even rules:
|
For me, I think your point is the same way as saying that using |
In general, turning errors into values doesn't lead to bugs, at least not in TypeScript. Go and Rust use two types of error handling in parallel:
In JavaScript, and especially in TypeScript, we have analogs for both of these features:
The main issue is that neither the JS nor TS codebase maintainers currently have explicit rules about when to use which approach. Some time ago, the TypeScript team conducted an analysis that showed even mature and widely used libraries don't document their error-handling strategies well enough. This proposal, not a turning errors to values, leads to bugs ;) |
Either way, I'm leaning in favor towards a Wdyt? |
Common!!! You work alone and touch the same file single time during entire project life??? Perhaps we should define what does "implicit" or "explicit" means. Implicit error ignoring means that developer is not warned about potential incorrect error handling and doesn't suppress this warning explicitly to express the intent to ignore the error. The expression bellow is also not suggested by any developer tool to be used, but currently eslint alarms here with try { doSomething(); } catch {} So, the developer is forced to ignore this linting rule here.
Yes, using And yes, you know, using |
It brings a naming issue. The term const [fetchOk, fetchResult] = try fetch(...); it doesn't seem to be a good naming approach ) |
...
Then the same can be done for this proposal.
Yes it will, what I was trying to explain is the following: Miss use of
That's why its not { ok, result }, a tuple can be destructured to any name. |
Sorry for the familiarity. I just want to say that sometimes things just go wrong. We spend a lot of time, effort, and money ensuring code quality, yet we still encounter many bugs and 'non-clean' code. So, we SHOULD presume such misuse.
The problem is that the proposal is used incorrectly and that leads to bugs.
No! We had not. We use
When we are talking about And the object here is better: const fetchResult = try fetch('....');
if (!fetchResult.ok) {
throw fetchResult.error;
}
const response = fetchResult.value; is much more readable. We don't use the same name for different entities. |
Again, I don't see a world where "forgetting to handle the error" is an issue.
|
The proposal is not being used incorrectly. This is programming and any dev can do anything it wants and there's nothing we can do to avoid it. The only time something can be done "wrongly" in your terms is when someone explicitly writes something to handle errors and then explicitly ignores the error. But I can argue that if 2 things were explicitly written, its not an implicit behavior. |
Is also a pattern that allows both of these use cases. const [ok, error, data] = try ...
if(ok) {
data.something
} else {
error.something
}
const [ok, result] = try ...
if(ok) {
result.something
} else {
result.something
} But as I said above, a poll should be raised to figure a better solution. |
That's your preference, and its ok. We could also make the try operator return a object that implements iterable, which would allow for both tuple and object usage. |
Please compare with a regular We cannot omit |
I don't see a reason to why not propose a linting rule after this proposal gets accepted. I just don't why turning errors into values is dangerous. |
I don't think this is an accurate view of what a lot the desire is for simpler try syntax. In many, many cases the friction with try/catch isn't the error handling code - which you can see by many of the examples in this proposal having very similar error handling structure as the analogous catch block - the friction that we want to avoid is with the success case for simple assignments, while still handling errors. Optimizing the syntax for that is great. To argue my point that errors should not be trivially turned into values, I'll start from some points that I hope are relatively uncontroversial:
With these points I don't think a solution for the syntax overhead of try/catch has to lead to a syntax that's more dangerous. Instead, we can solve the actual pain point and make safe assignments easier while still syntactically encouraging handling of the error. A shape of that type of solution might be to allow for the try part to be an expression and require the catch block to still be a block. That's why I'm suggesting something like: // Try is an expression we can use in assignments, but
// we still get a catch block to handle errors in
const value = try foo() catch (e) {
// handle errors here.
console.error('foo() threw', e);
}; Ignoring errors is still as obvious as with try/catch blocks: // You must include the catch block
const value = try foo() catch (e) {
// ignore error
};
If such a syntax were added to the standard, many people would add such a linting rule banning this syntax as unsafe. That's a sign that the syntax itself could be a lot better. |
This proposal must prepare the criteria for this rule. And it must be designed with minimizing this criteria and rules required.
it will work only for the JS. For TS it is impossible to specify the Iterator type with different type, only with a single type, so the value will always be |
@DScheglov the same way tuples are literables that have their typings ordered, this could be a case for it as well. We propose and improve JS and then TS aligns itself with the language, not the opposite. |
I think that the very idea of automatically turning errors into values is dangerous and should not be added to the language. It would make it far too easy to ignore exceptions and cause silent failures.
It's still useful to improve try/catch ergonomics, but I think that any proposal should only allow for propagating an exception or handling the exception, not accidentally ignoring it.
The problem is the potential for this:
It's nice that I could use
const
on the value variable instead oflet
, but that's a small benefit.What I really want is a way to make the success case much more ergonomic, while still giving me a block to handle exceptions in. Maybe something like:
Ideally there would be a way to return a new value from the
catch()
block, similar to howPromise.catch()
works.I'm not sure if some of the previous try-expressions proposals have covered this ground.
The text was updated successfully, but these errors were encountered: