Repository navigation
Talk about Exceptions Here #56365
Description
Activity
- addedDiscussionIssues which may not have code impactIssues which may not have code impact
on Nov 10, 2023 I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.
I love the irony that a maintainer was forced to check this box 😄
Reacted by Obed, Michael Angelo Rivera, Kyle Venn, Angelo Rivera, Cézar Augusto Nascimento e Silva, Josh Ghoulberg 👻, Reece Como, Sahin Deniz, Lazar Ljubenović, eklenin and 8 moremichaelangeloio commented
on Nov 27, 2023 More actionsKyle Kashuba (@KashubaK) Kyle Venn (@kvenn) I'm going to start looking at intellij (and also see if I can make an eslint plugin for it!). If you want to help, let me know!
Reacted by Kyle Venn, Marek Lukáš, Angelo Rivera, Tim Etler, Aylon Carrijo and ZefirWould be happy to be involved. I did like the idea of ESLint plugin because it's piggybacking off of an already established static analysis solution, but I think IDE plugins also check that box.
This comment has some code for the starting point of the ESLint plugin (in a collapsed text block): #13219 (comment)
michaelangeloio commented
on Jan 6, 2024 More actionsWould be happy to be involved. I did like the idea of ESLint plugin because it's piggybacking off of an already established static analysis solution, but I think IDE plugins also check that box.
This comment has some code for the starting point of the ESLint plugin (in a collapsed text block): #13219 (comment)
Kyle Venn (@kvenn) see my comment here about eslint michaelangeloio/does-it-throw#70 (comment)
I've got jetbrains-intellij working now, but waiting on jetbrains to approve it! You can check the code for that here if you'd like: https://github.com/michaelangeloio/does-it-throw/tree/main/jetbrains
Reacted by Huynh Duc DuyHeck yes! I'll happily use the IntelliJ plugin. I'll check back in a bit and install when it's approved.
Shame that eslint doesn't support async and that it's a ways away. But there's even more you can do with a plugin.
Nicely done!
Reacted by Michael Angelo RiveraReacted by Michael Angelo Rivera and Angelo RiveraKyle Venn (@kvenn) jetbrains is now available! https://plugins.jetbrains.com/plugin/23434-does-it-throw-
Feel free to share with others!
Reacted by Liam Jones, Kyle Venn and lokiI've since gotten the opportunity to try out the JetBrains plugin for does-it-throw and after looking into it a bit more, I don't think that really solves the problem I'm having with exceptions.
That plugin seems to mostly be about alerting of where throw statements are used. Which appears to be for enforcing that you don't use throw statements. I think throw statements are here to stay, even if I agree first class support for errors has advantages. And that if you're already in a codebase which relies on
throws, this adds a lot of noise.I had proposed an ESLint exception to warn when invoking a function that can throw. Encouraging you to either mark(document) this function as one that re-throws or to catch it. With the intention being to prevent you from accidentally having a function that throws bubble up all the way to the top of your program. But allowing that to be the case if it makes sense (like in a GraphQL resolver, where the only way to notify Apollo of the error is via throwing, or a cloud function / queue where throwing is used to retry).
If it can be found implicitly (without documentation), that's better. And it seems like a plugin could actually achieve that (and even offer quick fixes, which would be SO COOL). I'd advocate for using an already used standard TSDoc annotation (@throws) as the acknowledgement that this throw statement is there on purpose (as opposed to introducing a new one -
@it-throwsor@does-it-throw-ignore).does-it-throw has some great bones. And it seems like it's solving a problem for others, it just might not be the right fit for me.
Reacted by Nick Chevsky, Kyle Kashuba, zroug, Huynh Duc Duy and Luca VisentinKyle Venn (@kvenn) I've tried my hand at an eslint plugin: https://github.com/Kashuab/eslint-plugin-checked-exceptions/tree/main
It introduces two rules:
uncaught-errors
Checks to see if a function you're calling has a
@throwsJSDoc annotation. If you don't wrap the function call in atry/catchit will output an error.undocumented-errors
Warns you if a function has a
throwstatement without a corresponding@throwsannotation. Matches based on what you're throwing, in case you have custom error classes.Check out the tests for examples and what it covers. It's been a while since I looked at this, but I remember it being a bit buggy (i.e. nested branches, complicated logic) so there's a ton of room for improvement. I might take another look to improve it.
Side note - the README suggests you can install it from NPM, this is not the case haha.
(This is my work GH account, dunno why I have a separate one but oh well. I previously contributed here as Kyle Kashuba (@KashubaK))
Reacted by Kyle Venn, Vladyslav Zubko, dQw4w9WgXcQ, Huynh Duc Duy, Grégoire Geis and lokiReacted by Kyle Venn, Ian Luca, dQw4w9WgXcQ, Sebastian Garrido, Ray Thurn Void and lokiI'm definitely a complete noob when it comes to how Javascript/Typescript works, but would it be possible to add the modifier
throwsto a Typescript function signature?https://docs.oracle.com/javase/tutorial/essential/exceptions/declaring.html
As both a Typescript user and a library maintainer, I hate not being able to consume / deliver proper error declarations. I know I can use JSDoc's
@throws, but having that keyword as part of the function signature would be so great...Mateo (@wiredmatt) That suggestion was discussed in detail in the linked issue: #13219
The TL;DR is essentially, it's not worth doing because there isn't sufficient existing practice/documentation/runtime behavior to facilitate such a feature. TypeScript is designed to fit within the scope of JS' behavior, and since JavaScript doesn't give us reliable, native tools for things like checked exceptions it's challenging to fit it within scope.
What we're pondering now is, what's the next best thing? How can we encourage better error handling practices enough that the community has some common ground to operate on?
Reacted by Roy Kathurima and mercuryteaKyle Kashuba (@KashubaK) thank you for your response.
I was thinking about the newly introduced type guards / type predicates, in my mind it seemed totally possible, especially knowing we have conditional types as well.
I'll keep an eye on the eslint solution, that makes sense to me knowing what you just explained. thanks!
I was thinking about actual "throws" keyword as optional in return type, so TS would automatically add defaults to current functions/methods .. something like
throws<any>orthrows<unknown>at least for starting point.
so when we do write modules we can actually more strictly expose what type of things we are throwing out like example.function doAuth(): AuthPayload throws<TypeError | AuthError> {}
and maybe have some utility type similar as typeof to extract errors types from function so we can more easily utilize other modules throw types without directly importing those.
function someStuff(): AuthPayload throws<throwof doAuth | FatalError> {}
Also maybe this have later impact on catch argument type to actually know throw types, but just actual documentation of throw types is way more important atm for interoperability between modules as currently we are just quessing and reading module source code to undestand what might actually get thrown.
Edit:
this would also work for indication that function will never throw ..throws<never>Reacted by MateoRyanCavanaugh commented
on Mar 4, 2024 MemberAuthorMore actionsAlso maybe this have later impact on catch argument type to actually know throw types
This doesn't really work unless you have a level of information that doesn't exist in the real world, and requires the ability to express many patterns that are basically arbitrarily complicated. See #13219 (comment)
but just actual documentation of throw types is way more important atm for interoperability between modules
If documentation is the issue, you don't need a TS keyword -- https://jsdoc.app/tags-throws has existed for ages. I don't know about you, but I really don't see it used very often. This is the heart of the problem Ryan described in the comment linked above (summarizing the original issue): JS developers don't, broadly speaking, document expected exception behavior, so there's a chicken and egg problem where trying to implement checked-exception types would go against the grain of the current ecosystem.
Use of the
@throwsJSDoc tag can be treated as a sort of demand signal for better exception handling. Poor@throwsadoption indicates that the community doesn't want it. And without good@throwscoverage, athrowskeyword in TS wouldn't be very useful in the best scenario, and would be actively misleading at worst, giving devs the impression that they've handled the "expected" throw scenarios when they haven't.All that said, I still think there could be a place for some limited ability to perform static analysis of exception/rejection handling. I originally found the previous issue when I enabled a linter rule that looks for unhandled Promise rejections, which overlaps with try/catch once
awaitenters the picture. I was looking for a way to decorate some async function calls as being unable to throw or reject (thinkreturn Promise.resolve('static value')), which would let me build out exception-safety from the bottom up, slowly. Maybe this could work if we split the feature into declaring or asserting throws-type (basically the@throwsJSDoc tag), with a separate keyword or directive for enabling exception checking:/** unsafe-assertion-that-this-throws-strings */ function throwsSometimes(): number { if (Math.random() < 0.5) { throw 'nope!'; } return (Math.random() < 0.5) ? 0 : 1; } /** unsafe-assertion-that-this-throws-never */ function throwsNever(): number { return JSON.parse('2'); } /** checked-assertion-that-this-throws-never */ function maybeSafe(): number { return throwsSometimes() || throwsNever(); // error, unhandled throws-strings does not match declared throws-never }
Note that this is a different scope from what was discussed in the checked-exceptions section of #13219 (comment). I'm trying to statically analyze that explicit/declared
throwspropagate correctly up the chain, and importantly, to limit where those checks are performed. I want to be able to decorate one function as not calling functions with decorated/expected exceptions outside of atryblock -- to annotate one function as "exception-prone" and another as "bad at exception handling". I think there's value in that even if most library functions I call don't (currently) have their expected exceptions documented. (In Ryan's terminology, this requires "option one", unannotated functions are assumed not to throw anything.)88 remaining items
And if you don't plan to talk about exceptions, it might make sense to move your responses to a different thread.
We are talking here about Typed Exceptions.
The position of TS team is that
unknownis an exact type for exceptions.
But really there are cases when we need a typed errors and we need to utilize type system to check that everything is handled correctly.In TS -- exceptions are not a good way to solve this, considering current ecosystem state. The
Resultis not a unique way to solve that.The solution is simple -- don't throw in non exception cases, and that it. And it is also talking about the exception.
Now it looks like such frameworks like
tRPCuse exceptions ingotostyle -- and then we have arguments here that we need to type errors.Regarding this:
... TSDoc is the only way to declare it, which has limitations and isn't exactly bullet-proof, since a single typo can break it. ...
-
yes, it is not a bullet-proof,
throwsin Java is also not a bullet-proof, because in any moment it could be something like:throws Throwable-- the same asthrows unknown. -
no, the typo doesn't break anything. Like any typo in the documentation as well. Yes, it will complicate development, but it will not break the program.
Reacted by snarbles2Reacted by Kyle Venn-
You really can't explore a problem space without examining the tangential issues. Discussing other solutions and where and when exceptions don't need to be "solved" adds at least as much value as rehashing the same points again for the nth time.
Reacted by ravshansboxReacted by Dmytro ShchehlovSee it in the TS Playground.
The correct way to type
playwould be:interface HTMLVideoElement { play(): Promise<void, DOMException<"NotAllowedError" | "NotSupportedError">> }
So, no. We can't use JSDoc
@throwsin this case, because:- It doesn't throw. It returns a promise which rejects.
- You can't add error type to Promise. TS will error that it only takes 1 generic param.
- Even if it wasn't a Promise but a regular throw it would be ignored by TS anyways.
Now, if TS was actually checking the throw types it would also tell you there's no such class as NotAllowedError.
That was an excellent example of the quality of the documentation we are gonna get without TS support for checking throw types. Thank you <3
Reacted by Luca Visentin, Marco Nassabain and Boian IvanovReacted by Kyle Venn, Marco Nassabain and Boian IvanovReacted by Kyle Venninterface HTMLVideoElement { play(): Promise<void, DOMException<"NotAllowedError" | "NotSupportedError">> }
yes, you are right, I didn't check the correct type for this cases. It must be just
DOMException:interface HTMLVideoElement { /** * Loads and starts playback of a media resource. * * @throws `DOMException & { name: "NotAllowedError" }` if the user agent (browser) or operating system doesn't allow playback of media in the current context or situation * @throws `DOMException & { name: "NotSupportedError" }` if the media source (which may be specified as a MediaStream, MediaSource, Blob, or File, for example) doesn't represent a supported media format * * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) */ play(): Promise<void> }
JSDoc doesn't have the
@rejects, but Monaco Editor understands it as well. But it doesn't change the approach, as I said before: any "typo" in documentation doesn't break the program.Reacted by snarbles2 and KangKangReacted by Kyle Venn and Marco NassabainchristopherGdynia commented
on Sep 23, 2025 More actionsHello all,
i have a weird ts behaviour
the error message appears when running tsc
~project\node_modules\typescript\lib\_tsc.js:123141 throw e; ^ Error: Debug Failure. False expression: jsx should have errors when reporting errors at getSignatureApplicabilityError (~project\node_modules\typescript\lib\_tsc.js:76047:15) at resolveCall (~project\node_modules\typescript\lib\_tsc.js:76495:25) at resolveJsxOpeningLikeElement (~project\node_modules\typescript\lib\_tsc.js:77292:12) at resolveSignature (~project\node_modules\typescript\lib\_tsc.js:77334:16) at getResolvedSignature (~project\node_modules\typescript\lib\_tsc.js:77351:20) at checkJsxOpeningLikeElementOrOpeningFragment (~project\node_modules\typescript\lib\_tsc.js:74639:17) at checkJsxSelfClosingElementDeferred (~project\node_modules\typescript\lib\_tsc.js:74140:5) at checkDeferredNode (~project\node_modules\typescript\lib\_tsc.js:86652:9) at Set.forEach (<anonymous>) at checkDeferredNodes (~project\node_modules\typescript\lib\_tsc.js:86617:27)The error happens when adding a check before a type:
export type ObjectKeysOfType< T extends object = object, P extends unknown = unknown, - > = InnerObjectKeyOfType<T, P>; + > = keyof T extends never ? string : InnerObjectKeyOfType<T, P>;
In the editor are no errors
Info:
-
node:v22.17.0 -
yarn:1.22.22 -
typescript: tried both5.8.3&5.9.2
As context, error happens in a workspace library which is part of a monorepos with 30 Frontends (NextJS)
I don't know if we have to much type inference or stuff happening but I didn't saw this error message anywhere
I am interested in your thoughts.
Kind Regards Christopher
Reacted by Jendrik, Brett Wood, Thomas Berrios, Milanin and Kevin Ramharak-
Hey all - this might be of interest!
I’ve been experimenting with what typed, checked exceptions would look like if they were built into TypeScript.
I know this direction has been previously declined, but I built a prototype anyway as an experiment to explore the trade-offs in practice and how it feels in real code.
Instead of trying to model all possible runtime errors, it only type-checks the exceptions that are part of the program’s declared model - similar to how TypeScript treats types vs
any.The prototype:
- tracks thrown and rejected types
- enforces handling at call sites (try/catch, await, .catch, etc.)
- integrates with TS-style inference and assignability
- supports incremental adoption via a compiler option + new
// @ts-expect-exception suppression - can be used in library declaration files via
throws/rejects
I made a lot of intentional design decisions to make this feel as "TypeScript-native" as possible, but I think there are still some obvious tensions with encouraging exceptions as control flow, which I discussed here.
Playground: https://errorscript.jameshw.dev/playground
ES-demo.mov
I would appreciate any thoughts or feedback 🙏
Reacted by Dmytro Shchehlov, Trevor McCauley, Vladyslav Zubko, Luiz “Felds” Liscia, Kevin Ramharak, Niki, Boian Ivanov and Ben BeckerHey James Haworth Wheatman (@JamesDHW),
Really cool experiment.
I found a case that does not seem to be covered:
declare function throwsTypeError(): void throws TypeError; declare function throwsError(): void throws Error; declare function controller(callback: () => void throws never): void; controller(() => { throwsTypeError(); throwsError(); });
There is no error on the
controllercall, but the callback contract is definitely violated.Minor note: the Playground's "Copy Link" button also seems to generate links with the
/playroute instead of/playground.Reacted by James Haworth WheatmanHey Dmytro Shchehlov (@DScheglov), thanks for the quick feedback!
I have fixed:
- the assignability error
- the share link
Reacted by Dmytro Shchehlovethanresnick commented
on May 3, 2026 ContributorMore actionsJames Haworth Wheatman (@JamesDHW) Love this!
One thing I notice that isn't covered in your docs (and that seems to be at the core of Dmytro Shchehlov (@DScheglov)'s example) is function assignability in the presence of a
throwsclause. So a simpler version of the example would be:declare function throwsError(): void throws Error; declare function throwsSyntaxError(): void throws SyntaxError; let x = throwsSyntaxError; x = throwsError; // should this be allowed
The current playground does allow it, and I think that may be the right choice, but it means that the error Dmytro Shchehlov (@DScheglov) is hoping to get is intentionally out of scope.
Also, on the off chance you missed it (because this thread is so freakin' long), I made a similar proposal a while back that might be of interest: #57943
I think there are two related but separate questions here:
- What should happen when a
let-declared function variable is reassigned to a
function with a widerthrowstype? - What should happen when a callback is contextually typed by a parameter whose
contract saysthrows neverorrejects never?
For the first case, let's improve your example with a nominal-like custom error type (
Erroris assignable toSyntaxError):class CustomerSyntaxError extends Error { name = "CustomerSyntaxError" as const; } declare function throwsError(): void throws Error; declare function throwsSyntaxError(): void throws CustomerSyntaxError; const e: CustomerSyntaxError = new Error(); // (2322) Type 'Error' is not assignable to type 'CustomerSyntaxError'. let x = throwsSyntaxError; x = throwsError;
Considering that regular TypeScript allows this:
let x: 10 | 20 = 10; // x: 10 | 20 x += 1; // x: number
it makes sense to allow thrown exception types to widen for variables declared with
let.However, in this case, after the assignment, the type of
xmust be inferred as
function (): void throws Errorinstead of the current
function (): void throws CustomerSyntaxError.The
controller(cb: () => void throws never)case seems different. It is more similar to this:declare function fn(y: 10 | 20): void; let y: 10 | 20 = 20; fn(y + 1); // ~~~~~ // (2345) Argument of type 'number' is not assignable to parameter of type '10 | 20'.
The
controllercase is important and should not be intentionally excluded from the scope, because declaring an error boundary is one of the motivations for typed exceptions, because, it allows to define a place where developers are forced to handle errors and ensure their code does not throw or reject.For example, an Express-like application could declare its
getmethod so that route handlers must not throw or reject:interface Application { get( path: string, handler: { (req: Request, res: Response, next: NextFunction): void throws never; (req: Request, res: Response, next: NextFunction): Promise<void> throws never rejects never; }, ): void; } declare function parseUuid(input: string): input is UUIDString throws UUIDParsingError; declare function findUser(id: string): Promise<User> rejects UserNotFoundError; app.get("/users/:id", async (req, res) => { const userId = parseUuid(req.params.id); // ~~~~~~~~~ // The handler violates the `rejects never` contract const user = await findUser(userId); // ~~~~~~~~ // The handler violates the `rejects never` contract. // // Actually, ErrorScript reports an error here even if `.get` allows // rejection with Error, which is also incorrect. res.json(user); });
Reacted by James Haworth Wheatman- What should happen when a
ethanresnick commented
on May 6, 2026 ContributorMore actionsThe controller case is important and should not be intentionally excluded from the scope, because declaring an error boundary is one of the motivations for typed exceptions, because, it allows to define a place where developers are forced to handle errors and ensure their code does not throw or reject.
Dmytro Shchehlov (@DScheglov) What about a case like this?
declare function throwsError(): void throws Error; declare function throwsSyntaxError(): void throws CustomerSyntaxError; function callSafely(cb: () => void throws CustomerSyntaxError): void { try { cb(); } catch(e) { if(e instanceof CustomerSyntaxError) { return fallback; } throw e; } }
I think you'd say that the inferred throws type of
callSafelyisnever. In that case, this should be legal:controller(() => { callSafely(throwsSyntaxError); });
However, presumably you would not want this to be legal, because the thrown error will actually not get caught at runtime:
controller(() => { callSafely(throwsError); });
But now — if we stick with the idea from earlier that
x = throwsError;is allowed — then we're saying that one can assignthrowsErrorto a let variable declared/initialized astypeof throwsSyntaxError, but cannot assignthrowsErrorto a function argument with that same type!I don't think it's workable to have different rules for assignability to a variable as to a function arg, etc. If you wanna say that
throwseffects assignability everywhere, then ok, usingthrows nevercan work becausecallSafely(throwsError);just wouldn't be allowed. (Although the TS team has pushed back on error types influencing assignability for other reasons.) But I don't think you can say that assignability depends on whether it's to a callback or a variable.Hey Dmytro Shchehlov (@DScheglov) and Ethan Resnick (@ethanresnick),
Thanks for the support on this - sorry I've taken a while to get back (I've had to think about it)!
I think I understand the concern, and to confirm, in the latest version of ErrorScript, effects are included in function assignability.
TS only allows "widening" if the two types are assignable, so this is OK and expected:
let num: 10 | 20 = 10; // x: 10 | 20 num += 1; // x: number ✅
However, this gives an error (and is also expected):
class CustomSyntaxError extends Error { name = "CustomSyntaxError" as const; } const e1: Error = new CustomSyntaxError(); // OK ✅ const e2: CustomSyntaxError = new Error(); // ERROR: (2322) Type 'Error' is not assignable to type 'CustomSyntaxError'. ✅
Since
CustomSyntaxErroris assignable toErrorbut not vice versa.The same applies to effects:
() => void throws CustomSyntaxErroris assignable to() => void throws Errorbut not vice versa.This means we can have code like this:
class CustomSyntaxError extends Error { name = "CustomSyntaxError" as const; } declare function controller(callback: () => void throws never): void; declare function throwsError(): void throws Error; declare function throwsSyntaxError(): void throws CustomSyntaxError; function callSafely(cb: () => void throws Error): void { try { cb(); } catch(e) { if(e instanceof Error) { // ✅ changed impl here such that the inferred type of `callSafely` is `throws never`. return; } throw e; } } function callSafelySyntax(cb: () => void throws CustomSyntaxError): void { try { cb(); } catch(e) { if(e instanceof CustomSyntaxError) { return; } throw e; } } controller(() => { callSafely(throwsSyntaxError); }); // OK as callSafely acecpts any Error ✅ controller(() => { callSafely(throwsError); }); // OK ✅ controller(() => { callSafelySyntax(throwsSyntaxError); }); // OK ✅ controller(() => { callSafelySyntax(throwsError); }); // ERROR: Call signature thrown types 'Error' and 'CustomSyntaxError' are incompatible. ✅
Playground with implementations.
The last case errors because
() => void throws Erroris not assignable to() => void throws CustomSyntaxError(because fore: Error,ecould also be thrown asTypeError,RangeError,ReferenceError,..., due to the fact that these are all subtypes of error).callSafely(throwsSyntaxError)is OK, because based on my understanding,e instanceof Errorwill also catchCustomSyntaxError, so it doesn't drop anything.I don't think it's workable to have different rules for assignability to a variable as to a function arg
I agree on this one – I want things to feel "TS-native" in terms of assignability.
So I think the current implementation now behaves consistently for:
- callback parameters
- contextual typing
- throws never boundaries
- assignability
without introducing different rules for variables vs callbacks.
Please let me know if I'm missing some problematic edge case here – this discussion has been extremely useful for clarifying my understanding so far!
n.b. Ethan Resnick (@ethanresnick) - your proposal looks very interesting with
UnknownErrorto account for the unavoidable error cases.I couldn't implement this as-is in ErrorScript because it would make the
throws nevercase unreachable, which would break the exception checking model. However, there could be some middle ground where everything that doesn't throwneveralso unionsUnknownError, which would force developers to account for unexpected errors whilst keeping useful exception checking behaviour.Reacted by Luiz “Felds” LisciaJames Haworth Wheatman (@JamesDHW) This is epic
Reacted by Luiz “Felds” LisciaReacted by James Haworth Wheatmanwebdevpraveen commented
on Jul 5, 2026 More actionsI've been following this thread for a while now.
adding something like a
throwssignature (or leaning into aResult<T, E>pattern) sounds great for strict enterprise codebases, but wouldn't it make the learning curve way too steep for everyday React devs?just wondering if baking this natively into the compiler might tank the type-checking performance. maybe standardizing a
Resultpattern at the library level first (like how rust does it) could be a safer middle ground?Praveen Kumar Singh (@webdevpraveen)
but wouldn't it make the learning curve way too steep for everyday React devs
I think you're underestimating React devs…
Every one using
throwis already internalizing that a function can exit in more than one way. The annotation would only encode that formally. Also, the types would ideally be optional, and inferred from code, just like it happens today withreturn.maybe standardizing a
Resultpattern at the library level firstThat can already be done in userland. In fact, it's quite easy to implement using a tagged union; the problem is standardizing it across the whole ecosystem:
type Result<T, E> = | { ok: true; value: T } | { ok: false; error: E };
Unfortunately, that's not how existing code works, and it's clunky to use without the
?operator, since thethrowsemantic is to bubble up.Generating that would be out of scope from TypeScript, since it follows the principle that type annotations should not affect runtime behavior — they exist only at compile time and are erased from the generated JavaScript.
Reacted by James Haworth Wheatman
Acknowledgement
Comment
#13219 is locked so that the conclusion doesn't get lost in the discussion, so talk about exceptions here instead