-
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 type annotation on catch clause variable #20024
Comments
I do like the idea of |
I agree, it is possible to safely annotate the error variable in many situations. Older discussion in #8677 (comment). It also true that it's easy to misunderstand/abuse, but then so are type annotations in general, and people just do the cast in the first line of the catch block anyway. |
@jaredru code like
is dangerous and misleading. It only passes the type checker because the parameter of the catch method's callback is declared to be Promise.reject(NaN).catch((e: Error) => e.message.length); |
I disagree. It is no more dangerous or misleading than The irony of your example is that it throws a |
I don't think that is a good pattern either. const foo = <Foo>JSON.parse(input); Is much clearer as to intent. As for the example, contrived as it may be, it doesn't matter what gets thrown ( |
Sorry, consider it a typo.
Of course it matters. That the runtime throws an |
My 2ct: If there is any party that should be able to type the exception it should be the called function, not the caller. I've seen codebases that throw strings everywhere or nothing at all ( if (err instanceof MyError) ... else throw err
switch (err && err.code) {
case 'ENOENT': ...
case 'EPERM': ...
default: throw err
}
const isExpectedHTTPError = (err: any): err is HTTPError => err && typeof err.status === 'number'
if (isExpectedHTTPError(err)) {
res.status(err.status).set(err.headers || {}).send(err.body || '')
} else {
res.status(500)
} And if the check doesn't pass, rethrow the error. If you don't and your usage of the error doesn't directly produce some kind of TypeError then you might swallow unexpected programming errors and create a debugging nightmare. Is this more code to write? Yes. But does that justify a new feature to make the unsafe version easier to write? |
My argument is for pragmatism. One of the stated non-goals of TypeScript's design is to "Apply a sound or 'provably correct' type system. Instead, strike a balance between correctness and productivity." Yes, I could get something besides an As in the TypeScript codebase itself (1 2 3 4 5), it is very often safe and reasonable to assume that the thrown value is not, in fact, |
TypeScript language is already the best I've ever worked. |
I'd like to at least be able to do
|
I would certainly love a |
need this |
It's not productive for me to guess which properties are on the Error and have to look it up. Please implement. |
I think it would be awesome ! |
I like @Andrew5569 's Idea, but want to have the ability to define it like I want: try {
// ...
} catch (err: unknown) {
// ...
}
try {
// ...
} catch (err: Error) {
// ...
}
try {
// ...
} catch (err: AWS.AWSError) {
// ...
} |
@Shinigami92 It will never be possible. TS is being transcoded to Javascript for execution. And Javascript does not implement the type. What would be possible is
|
@GabrielDelepine sorry you get me wrong. I only want to assume the type, not to define it. |
Yeah, please just fix this :-) The fact that this is not allowed is both inconsistent and inconvenient: try { ... } catch(error: Error) { ... } Especially considering that this is allowed: promise.catch((error: Error) => ... ) You can't argue that allowing it would be dangerous, just because people might incorrectly assume the type is Not to mention, that in the vast majority of cases, assuming that the error will in fact be an While you are at it, please also add an optional reject type to promises: interface Promise<TResolve, TReject = any> Because yes, we can, and often do, guarantee that an async function will always reject with a certain type - and arguing against that is kinda ridiculous. We can just wrap everything in the function in a For example, we have an These would not be breaking changes, they would provide a lot of value, and people have been asking for it for years - so please just implement it :-) |
I would love it if it were implemented with a new Ability to define and infer expected error typesfunction foo(): Throws<Error> { // should be inferred
throw new Error()
}
try {
foo()
} catch (error) { // <-- Error
// noop
} Throw something other than an
|
Why do you think your approach does not accept this? function foo(): Throws<Error> { // should be inferred
throw new Error()
}
try {
const err: Throws<Error> = foo()
} catch (error) { // <-- Error
// noop
} I also think that |
@Shinigami92 good point on the breaking change. I don't think your example is any different than the function foo(): never {
throw new Error();
}
try {
const err: never = foo()
} catch (error) { // <-- any (existing implementation)
// noop
} |
+1 for having type-check flow through |
At the very least, you should be allowed to use a type that extends the Error type, as is done with
|
In addition, you guys should allow multiple catch clauses that catch different types of exceptions--just like every other programming language supports, like so:
That would be awesome! Just sayin. I'm sure we'll get there one of these days but who knows how long it will take to get there in JavaScript/TypeScript... |
We already discussed this earlier try {
...
} catch (err) {
// do stuff
} catch (err) {
// do other stuff
} catch (err) {
// catch all
} How will you distinguish between the three different catch |
This is now a real pain in typescript 4.4 with
I would prefer to do the latter. Multiple catch blocks should be out-of-scope. |
Still see no reason exceptions cannot be typed like most other languages, especially when running TS in strict mode.
Additionally, adding a bunch of Multiple catch blocks would not solve this and should not be considered. |
Sorry to say, but you guys are wrong. Some years ago I was on the same track like you. And it does nothing to do with TypeScript, but with JavaScript and expectations. You can't just say @DanielRosenwasser has described the challenge short and precise in his comment. I think it would be better to lock this discussion, because there are no new conceptual proposals, but |
Nothing yet? |
@Bessonov I agreed with everything you said up to this point. By the points you've given, then My real issue with it is its inconsistency. If I were the TypeScript dictator, I would allow no type annotations in |
You could just cast to try {
// do something
} catch (error) {
console.error(error as Error);
} In TypeScript 4.4+, those errors are now |
This could be really useful for test suites after 4.4 defaults to unknown. In test suites, defaulting to unknown is not appreciated as generally you know which error you expect and even if for some reason the code threw anything else, it would pop up on the test anyway. |
It's possible. You can check error type inside So with this solution Actually it looks like a good solution. Or is there something wrong with it? |
@bodograumann As of TypeScript 4.4 you can set |
A nice way to safely unwrap your catch (e) {
if (e instanceof Error) {
// type-safe, compiler can help you in this block
console.log(e.message);
}
else {
// optionally
throw e;
}
} You're explicitly handling all possibilities, so it's type-safe. I think catch variable as unknown is a good default as it encourages you to think about the values that variable can take. It may make sense to have a tsconfig flag to allow |
Another (type safe) option is to allow to safely cast to something like:
where MaybeError is some custom type defined similar to:
I.e. allow This would work, because, when MaybeError is defined the above way, there is no JS thing which would not match it. Even null or a plain string or a number thrown will still match. |
TypeScript Version: 2.6.1
This was discussed in #8677 and #10000. It was closed as "fixed" in #9999, but as far as I can tell neither of the issues was actually resolved. In either event, I'd like to make a case for allowing type annotations in catch clauses.
Especially with the introduction of downlevel async functions, I'd suggest that disallowing catch clause type annotations leads to less safe code. In the example, the two methods of handling the promise are functionally equivalent, but one allows you to type the error, and the other doesn't. Without writing extra code for the
try
/catch
version (if (err instanceof Error) {
orconst e: Error = err
or something), you'll get a runtime error that you wouldn't get with the purePromise
version.The primary rationale for not allowing this is that any object can be thrown, so it's not guaranteed to be correct. However, most of the benefit of TypeScript comes from making assertions about your and other people's code that can't be strictly guaranteed (especially when importing JavaScript). And unless one would argue that the
Promise
catch function also shouldn't allow a type annotation on the error parameter, this argument seems to make very little practical sense.I believe one of the other arguments against is that it might be confusing, as it looks like the typed exception handling you might see in other languages (e.g., Java), and folks may think the catch will only catch errors of the annotated type. I don't personally believe that's a legitimate issue, but if it really is I'd propose at least allowing a
catch (err as Error) {
syntax or similar as a way of emphasizing that it's a type assertion.If nothing else at all, it seems that there should be a way to trigger a warning (similar to an implicit any warning) when using an untyped
err
directly within a catch block.The text was updated successfully, but these errors were encountered: