-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Allow unknown
type annotation on catch clause variable
#36775
Comments
This would be great (together with a lint that forces doing this always). Right now, catch (e) {} is basically a situation that should be forbidden by |
I would really love this as a compiler option. |
I think I would also prefer a new
I think type safety is not the only issue here. Unexperienced TypeScript developers would think that this catch statement might only handle errors of type |
👍 this would be amazing. To allow a type annotation on |
Assuming this is the appropriate place to discuss this, I would like to advocate that the change in 4.0 be either (in order of preference):
I understand that |
I am excited typescript 4.0 will address this issue. We had to add a specific |
It should probably also be possible to do: try {
// code
} catch (err: any) {
// error handling
} In case |
Why not to try infer the possible types of error? They are very well known to the compiler. Isn't it right that the
You may ask, why to handle all errors at the same place. For me, the reason was that Express. For every response you can only send headers once (, logically but annoying to handle an error with it when trying to parallel all async tasks).
|
Yeah, I'd like to have check exception in typescript but I think it might be easily misused. |
Misused? How so? |
Not handling the exception properly, popup them into the upper function instead. So you will get a tons of unioned exception types. Or someone writing "throws any" , then the whole work become useless. Or someone writing "throws Error", then all subclass of Error get merged into the widen type. |
Don't forget about stack overflow, divide by zero, and out of memory errors! Every function MUST throw those (unless you can prove one or more is impossible, which I think is only possible at compile time for divide by 0 in theory). Java made the mistake of not having every function throw those which really put a hamper on the utility of checked exceptions IMO. |
How do Java, rust or other language handle those three kind of error in their checked exception system? |
@Jack-Works I have only worked with Java's checked exceptions and you'll get a runtime exception after the compiler asserted no exception was possible. |
Enabeling types for errors does not change how user will use this.
Well now you are forced to have |
For JavaScript/TypeScript this would be solved by extending all thrown parameters with error... We are sure that a try catch block might throw Thus the end type might be something like |
I doubt adding checked exception is ever going to work in Typescript, because anytime you call an API you don't fully control you are adding I've previously tried doing this: Never use throw. Instead create this simple API: type CheckedReturn<R, E> = {ok: true, value: R} | {ok: false, value: E}
function Err<E>(e: E): CheckedReturn<any, E> { return {ok: false, value: E} }
function Ok<R>(r: R): CheckedReturn<R, any> { return {ok: true, value: R} } (optionally add some monadic functions like and_then etc akin to Rust Result) . Then you always do this: function foo(...): CheckedReturn<string, SomeError> {
// ...
return Ok(actualValue);
// or
return Err(errorValue);
} This sounds great in theory and gives you fully checked exceptions always... Except when you use literally any external or other API, and then you're back to where you started, except it's worse: you now have lots of additional effort, two layers of kinds of exceptions and still can get untyped / unchecked exceptions all the time. The best that can be done is making a union type of all your own possible checked errors (or inherited error classes if you're okay with not having exhaustive checking / open domain errors): type OwnErrorType =
| { kind: "FetchError", status: 404 | 500, message: string }
| { kind: "DatabaseConnectionError", reason: pg.ErrorCode }
// ...
// OR, if you don't care about exhaustiveness,
// and don't pass your errors through serialization so instanceof does not break
abstract class OwnError extends Error {}
class FetchError extends OwnError {
constructor(public status: 404 | 500, public message: string) {}
}
// ... and then on top of each catch block, add a assertion / condition to always instantly narrow your type and rethrow others: function assertOwnError(err: unknown): asserts err is OwnErrorType {
/// some check, using instanceof or brands or whatever
if (!err || !err.__isOwnErrorType) throw err;
// or: if (err instanceof OwnError)
}
// ...
try {
// ...
} catch (e: unknown) {
assertOwnError(e);
// error is correctly typed here
} This is then basically the same as catch blocks you expect from other languages: try {
} catch (OwnError e) {
// ...
} I'm already doing this in my own code bases, but to make it ergonomic these things are missing:
|
Since we know that every function in JS might throw
If the
Sadly you can't reliably extend error. What is wrong with giving the option? This won't prevent, but nor support error sinking... Error sinking just might be useful sometimes...
I would say that we should look at how much people assign the type to the parameter of the catch block. If it is high enough then, it might be right to allow the user to infer that information.
Sure, that's nice but it does not solve the issue at all... |
That's not actually true, since you can throw anything. |
@phiresky only if the type annotation (for throws) are missing... Then they get updated, they will gen narrowed down to a union that will not contain And since TS can parse type of all throw statement it could simply generate type definition files including all errors too! Including non-typed external programs sure will lead to problems but users often create types for what they are missing anyway. (At least I do) EDIT: The only time the compiler could not infer type for Please correct me, if I am wrong, but to me, the only reason we cannot have typed catch blocks is the amount of work that would go into it & unresolved syntax... |
We're using ts-results (https://github.com/vultix/ts-results) to use Rust style checked exception in our program. But it is not good enough cause TypeScript doesn't and won't have the Rust And another concern is that we cant use the checked exception with confidence, because the rest of the world, including the JS builtin library, the npm libraries... doesn't obey the checked exception way, they throw.
@Akxe: It won't work because the calling function might come from the npm library with a
@phiresky: Hi, please try ts-results, it's awesome! (I have made a PR to implement |
@Jack-Works sure not all libraries have type anotation, but with time (and this feature in place) they would come... Meantime and for some occasion, forcing throw type would be necessary... It is basically the same as saying TS will never be used because npm packages are not always written with types in mind... I do think this argument is invalid as it would be solved soon after releasing typed catch blocks |
In addition, allow an explicit `any`; anything else throws an error. Also adjust and reorganize existing tests. Fixes #36775.
Note that the above adds the ability to add an |
Awesome! I've made a PR to force adding a |
Just use array destructuring
If you want to ignore the error
|
@hazae41 Sure, but |
@hazae41 the go style doesn't work well in typescript cause the type system doesn't understand if error is none, the result must be valid. But the rust style is different, typescript do recognize this tagged union pattern therefore you can't miss the error handing |
Is this meant to be 'fixed' in 4.0.0-beta? |
@WORMSS This issue is to allow you to manually type like this: catch (err: unknown) |
Any way to force it to unknown? We have "noImplictAny" checked, which says "Warn on expressions and declarations with an implied 'any' type." But we get no warning that err is an 'any' type. |
you can use a future eslint version, hopefully maybe another issue should be opened here to add a |
Agree, definitely a new strict flag for it to treat it as unknown or enforce to write unknown |
They already said that you can write your own linter rule. Don't expect a flag for this... |
Guess someone will have to rewrite the description of noImplyAny to be |
For a 4.0 change (major version update) this certainly feels like it should be included in |
TS doesn't follow semver. The version is just an arbitrary number that's monotonically increasing. This release is 4.0 purely because 3.9 + 0.1 === 4.0 See: #14116 |
This doesn’t work in JavaScript with JSDoc type annotations: try {
// something
} catch (/** @type {unknown} */ err) {
// `err` is still typed as `any`:
err; // $ExpectType unknown
} Playground link: 🔗 |
…peScript 4, but version 3.6.4 is currently being used. microsoft/TypeScript#36775
The catch blocks have some "new" syntax we need to adhere to: microsoft/TypeScript#36775 As of Feathers 4, the request and response body are actually of type any, but we have to specify that explicitly. We can probably get rid of this `// @ts-ignore` now.
Search Terms
catch clause unknown exception
Suggestion
Now any kind of type annotation on catch clauses is not allowed.
In my understanding, it's because that it's unsafe to annotate like
catch (err: SpecialError)
since any value will be captured actually.However, due to the type of
err
isany
, it's also not type safe.So I suggest to allow annotating
unknown
, as most safe typing.(And annotation with any other type won't be allowed as is)
Examples
can be written safer as
Related Issue
#20024
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: