Typescript: Allow type annotation on catch clause variable

Created on 15 Nov 2017  ·  58Comments  ·  Source: microsoft/TypeScript

TypeScript Version: 2.6.1

const rejected = Promise.reject(new Error());

async function tryCatch() {
  try {
    await rejected;
  } catch (err: Error) { // TS1196: Catch clause variable cannot have a type annotation
    // Typo, but `err` is `any`, so it results in runtime error
    console.log(err.mesage.length);
  }
}

function promiseCatch() {
  rejected.catch((err: Error) => { // OK
    // Compiler error; Yay!
    console.log(err.mesage.length);
  });
}

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) { or const e: Error = err or something), you'll get a runtime error that you wouldn't get with the pure Promise 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.

Add a Flag Awaiting More Feedback Suggestion

Most helpful comment

I think a better solution would be to allow typed catch blocks to be transpiled to if (typecheck) else throw:

try {
  doSomethingA();
  doSomethingB();
  doSomethingC();
} catch (e: string) {
  console.error(e);
} catch (e: number) {
  console.error('example error message:', e);
} catch (e: Error) {
  console.error(e.message);
}

this could then be transformed into:

try {
  doSomethingA();
  doSomethingB();
  doSomethingC();
} catch (e) {
  if (typeof e === 'string') {
    console.error(e);
  } else if (typeof e === 'number') {
    console.error('example error message:', e);
  } else if (e instanceof Error) {
    console.error(e.message);
  } else {
    throw e;
  }
}

All 58 comments

I do like the idea of catch (err as Error) because, damn it, if you were going to cast it anyway, maybe we should make your life a little easier while keeping it explicit.

I agree, it is possible to safely annotate the error variable in many situations. Older discussion in https://github.com/Microsoft/TypeScript/issues/8677#issuecomment-220495646.

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

function promiseCatch() {
  rejected.catch((err: Error) => { // OK
    // Compiler error; Yay!
    console.log(err.mesage.length);
  });
}

is dangerous and misleading. It only passes the type checker because the parameter of the catch method's callback is declared to be any. At best this is a disguised type assertion.

Promise.reject(NaN).catch((e: Error) => e.message.length);

I disagree. It is no more dangerous or misleading than const foo: Foo = JSON.parse(input);, which is a very reasonable pattern. It represents our expectations and assertions about the code we're dealing with.

The irony of your example is that it throws a TypeError, which gets at my underlying point. Aside from contrived examples, the expected currency for errors is Error. A simple search through the TypeScript repo itself shows that catch (e) virtually always assumes the e to be an Error. That doesn't make it "right", of course, but it highlights the realistic expectation of a large, real-world project that (presumably) reflects an understanding of the balance between correctness and pragmatism, particularly as it relates to TypeScript.

I disagree. It is no more dangerous or misleading than const foo: Foo = JSON.parse(input);, which is a very reasonable pattern. It represents our expectations and assertions about the code we're dealing with.

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 (TypeError or not) because it is thrown from the catch causing an unexpected failure.

Sorry, consider it a typo. <Foo>JSON.parse(s) is a common pattern but is as unsafe as .catch((e: Error) => {. Both treat an any as another type with no guarantee of correctness beyond the developer's word.

it doesn't matter what gets thrown (TypeError or not) because it is thrown from the catch causing an unexpected failure.

Of course it matters. That the runtime throws an Error for all runtime errors strengthens the position that it's reasonable to expect the argument to catch to be an Error. This expectation is common in real world codebases--including, but certainly not limited to, TypeScript's.

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 (undefined). Just declaring that the type is Error doesn't make it more type safe, because you never know what other functions the function you are calling may call into and what things those nested functions throw - Errors are not strongly typed and not part of the interface contract. Maybe you know it now but in the next patch version of some transient dependency it could change. So the only correct way to mirror the runtime JS is to start with anyunknown and narrow it down manually

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 Error, but in reality--in the projects I'm working in--I won't.

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, undefined (or null, or a string, ...) and is with almost certainty an Error. I would simply like the compiler to help me out in this case without additional code or runtime overhead.

TypeScript language is already the best I've ever worked.
If you type the catch clause this will make much better.

I'd like to at least be able to do

  try {
    await rejected;
  } catch (err: unknown) { 
    // ...
  }

I would certainly love a exceptionTypeUnknown flag that makes all exception types (and Promise rejections) type unknown instead of any. That would definitely make the error handling _safer_ as opposed to allowing any annotation. Although this could also be solved by writing a TSLint rule that forces them to always be declared unknown, if we had this annotation.

need this

It's not productive for me to guess which properties are on the Error and have to look it up. Please implement.

@DanielRosenwasser

I do like the idea of catch (err as Error) because, damn it, if you were going to cast it anyway, maybe we should make your life a little easier while keeping it explicit.

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

try {
  // ...
} catch (err: unknown | Error | AWS.AWSError) {
  if (err instanceof Error) {
    // ...
  } else if (err instanceof AWS.AWSError) {
    // ...
  } else {
    // ...
  }
}

@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 Error, when it can in fact be anything. If someone were to assumes that, they will just cast to Error anyway, which would be just as much of an error. And this could be said for any type used anywhere, so there's really no valid reason to treat errors as a special case.

Not to mention, that in the vast majority of cases, assuming that the error will in fact be an Error, _is_ actually perfectly reasonable. Please be a bit more pragmatic about those things, especially when it is something that gets in our way on a daily basis.


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 try block, and throw a custom error if we end up in the catch.

For example, we have an ApiClient, which guarantees that if it rejects, it will always be with an ApiError. It's _extremely_ annoying that we can't declare that fact in our types, but instead have to manually cast the errors everywhere we use it. That's neither practical, nor type safe.


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 Throws<TReason = Error> type and Promise<TResolve, TReason> for Promises.

Ability to define and infer expected error types

function foo(): Throws<Error> { // should be inferred
    throw new Error()
}
try {
    foo()
} catch (error) { // <-- Error
    // noop
}

Throw something other than an Error (e.g., a number)

function bar(): Throws<number> { // should be inferred
    throw 42;
}
try {
    bar()
} catch (code) { // <-- number
    // noop
}

Multiple expressions within a try block

try {
    foo();
    bar();
} catch (reason) { // <-- Error | number
    // noop
}

Promise

async function baz(): Promise<never, TReason = Error> { // should be inferred
    return Promise.reject(new Error())
}
async function qux() {
    try {
        await baz()
    } catch (err) { // <-- Error
        // noop
    }
}

Checklist

My suggestion meets these guidelines:

  • [ ] This wouldn't be a breaking change in existing TypeScript/JavaScript code
    > As pointed out by @Shinigami92, it would be a breaking change unless implemented as Throws<TReason = any>, but that's not really a sensible default, as discussed above.
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [x] This feature would agree with the rest of TypeScript's Design Goals.

@jedmao

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 Throws<TReason = Error> introduces a breaking change when it is inferred (and it should).
Throws<TReason = any> would not do this.

@Shinigami92 good point on the breaking change.

I don't think your example is any different than the never type.

function foo(): never {
    throw new Error();
}
try {
    const err: never = foo()
} catch (error) { // <-- any (existing implementation)
    // noop
}

+1 for having type-check flow through try/throw/catch code. This is driving me nuts currently for adopting async/await with try/catch where now I have no way of typing the error (with promises I could at least add a type).

At the very least, you should be allowed to use a type that extends the Error type, as is done with @types/mysql.

                try {
            // perform database fetch
        } catch (error: MysqlError) {
            if (error.code === 'ER_DUP_ENTRY') {
                return;
            } else {
                throw error;
            }
        }

The catch (err as Error) syntax seems related to (or is possibly a subset of) #10421

Is there any hope for this feature? (or something better)

I think a better solution would be to allow typed catch blocks to be transpiled to if (typecheck) else throw:

try {
  doSomethingA();
  doSomethingB();
  doSomethingC();
} catch (e: string) {
  console.error(e);
} catch (e: number) {
  console.error('example error message:', e);
} catch (e: Error) {
  console.error(e.message);
}

this could then be transformed into:

try {
  doSomethingA();
  doSomethingB();
  doSomethingC();
} catch (e) {
  if (typeof e === 'string') {
    console.error(e);
  } else if (typeof e === 'number') {
    console.error('example error message:', e);
  } else if (e instanceof Error) {
    console.error(e.message);
  } else {
    throw e;
  }
}

@zzzzBov

How about doing something similar except using instanceof? That way, it could mimic aspects of other languages that folks may be used to. If so, it would be nice if TS could at least warn if ordering of catch clauses caused one clause to become unreachable because a clause with a more general type is ahead of it.

@treynash

How about doing something similar except using instanceof?

I don't fully understand what you mean here. Are you saying it should not use instanceof at all or that it should only use instanceof?

it would be nice if TS could at least warn if ordering of catch clauses...

I definitely agree that a warning for unreachable code would be useful, however I'm not sure if that necessarily belongs in TS or whether that task should be delegated to a linter. I don't feel strongly one way or the other 🤷‍♂.

@zzzzBov

I should have been more precise. What I meant was that instead of if (typeof e === 'Foo'), it could be something like if( e instanceof Foo). That way, it should match anything that extends Foo as well as instances of Foo, which is what I meant by when I referenced the way some other languages work.

@treynash ah ok, Then I should point out that my example uses instanceof for Error. It _can't_ use instanceof for String or Number types because primitive values are not instances.

@zzzzBov
Yep, indeed. And I would imagine one would also want to be able to discern based on things like union and intersection types.

It's more verbose, but what about a syntax like c#'s exception filters: catch (identifier) when (expression) { statements; }, where identifier is in-scope in expression, and the same type guard semantics apply as in a regular if statement.

Maybe at that point you might as well just stick an if statement in your regular catch block, I suppose

Now that ESLint 7 is out with a new set of no-unsafe-* and the restrict-template-expressions rules, the untyped catch clauses make the lint output extremely noisy. The following short snippet contains a whopping 2 ESLint errors just because we are not easily able to assert that e is indeed an Error:

try {
    throw new Error("nope");
} catch (e) {
    console.log(`Had error: ${e.message}`);
    // ^-- Two lint errors here: 
    // Invalid type "any" of template literal expression. eslint [@typescript-eslint/restrict-template-expressions]
    // Unsafe member access .message on an any value. eslint [@typescript-eslint/no-unsafe-member-access]
}

I'm fine with allowing any in template literals, but the no-unsafe-member-access rule is actually very helpful. Since ESLint also makes sure that no non-Errors are thrown, I do like @DanielRosenwasser's idea of casting explicitly (catch (e as Error)).

Up .message should be available when you're in catch clause

I can't believe this is still an open issue. Seems like a very obvious feature for TypeScript. Can't wait to see it!

I personally prefer catch(error as Error) as opposed to catch(error: Error) because the first makes more sense because you're casting and faking it, but if we used the second method then people may mistakenly believe we are telling the try-catch block to behave in a certain way which is not what TypeScript is for. That should be a new JavaScript feature.

Still, @zzzzBov did show an interesting way of getting around this which is something I'm completely up for.

Now that ESLint 7 is out with a new set of no-unsafe-* and the restrict-template-expressions rules, the untyped catch clauses make the lint output extremely noisy. The following short snippet contains a whopping 2 ESLint errors just because we are not easily able to assert that e is indeed an Error:

try {
    throw new Error("nope");
} catch (e) {
    console.log(`Had error: ${e.message}`);
    // ^-- Two lint errors here: 
    // Invalid type "any" of template literal expression. eslint [@typescript-eslint/restrict-template-expressions]
    // Unsafe member access .message on an any value. eslint [@typescript-eslint/no-unsafe-member-access]
}

I'm fine with allowing any in template literals, but the no-unsafe-member-access rule is actually very helpful. Since ESLint also makes sure that no non-Errors are thrown, I do like @DanielRosenwasser's idea of casting explicitly (catch (e as Error)).

My ugly workaround for the new unsafe-* rules is just

catch (ex) {
    const error = ex as Error;
        logger.info('my error is meh', { error });
        logger.debug(error.message); 

I prefer this over disabling the rule for the line/file. still meh.

This is more-or-less addressed in https://github.com/microsoft/TypeScript/pull/39015, which will land in 4.0. Not perfect, but better than nothing.

@guidobouman I agree that #39015 is a small step towards this feature but in no way addresses it. That merge request is mostly does nothing to address #20024 IMO because it requires unknown or any. Even if that merge request did allow for types, it wouldn't solve this issue unless it caught only those types in the compiler.

Something like this is what we're looking for:

try {
  doSomethingA();
  doSomethingB();
  doSomethingC();
} catch (e) {
  if (typeof e === 'string') {
    console.error(e);
  } else if (typeof e === 'number') {
    console.error('example error message:', e);
  } else if (e instanceof Error) {
    console.error(e.message);
  } else {
    throw e;
  }
}

Right now it's a bit verbose. What if I want to catch some exceptions and have it to continue to throw if it's not the one I wanted?
Currently I have to:

  try {
    something();
  } catch (err) {
    if (err instanceof SomeError) {
      doSomethingElse();
    } else if (err instanceof AnotherError) {
      doAnotherThing();
    } else if (err instanceof YetAnotherError) {
      yetAnotherThing();
    } else {
      throw err;
    } // Else //
  } // Try, Catch //

What I wouldn't mind seeing is:

  try {
    something();
  } catch (err: SomeError) {
    doSomethingElse();
  } catch (err: AnotherError) {
    doAnotherThing();
  } catch (err: YetAnotherError) {
    yetAnotherThing();
  } // Try, Catch //

Which would just output what was above. Would save me some typing and be pretty convenient like in Java and other languages.

Multiple catches are not a pattern that is known to JavaScript. Feels weird to change the structure of the language.

Multiple catches are not a pattern that is known to JavaScript. Feels weird to change the structure of the language.

Neither is private/public constructor fields, or public/protected/private class fields, etc etc.. Need I go on?

Neither is private/public constructor fields, or public/protected/private class fields, etc etc.. Need I go on?

Microsoft did those features in the earlier days of TypeScript, but not long after decided to more closely align with the ECMAScript specification, which they have done extremely well since. Their design goals are clear and they will not be introducing features like this that deviate from the specification.

The whole point of Typescript is to type check as a transpiler to JavaScript, which is why we're not using vanilla JavaScript.

If you feel a feature could benefit many others, but dismiss it because it "feels weird" but makes sense in other languages and is successful and many use those features, why do you raise your voice against it?

We use Typescript for the sugar that it provides. Decorators? Typed variables? Type checks? Generics? Function overloads? Don't forget the new variadic tuple types in typescript 4.

The point is, it's meant for a more Java or OOP programming that helps you have more typed and safe control over your code. It will always run JavaScript, that's a given (as I think they stated that as a mission goal).

But we shouldn't feel weird when using a language that transpiles into another language if there's differences.. there will always be differences, and that's good.. Typescript !== JavaScript.

but not long after decided to more closely align with the ECMAScript specification

Can you provide a source reference to this decision? I can't seem to find it.

I'd like to be a part of that discussion. That's why I'm asking where you found it.

I don’t use TypeScript for the sugar it provides. The sugar is the very thing that is not in their design goals today. It’s more about confidence and type safety and all the type information disappears at compile time, as it should. Decorators are another one of the earlier features they implemented before ironing out the design goals, so it’s irrelevant to your argument.

I don’t think you’re going to get any traction requesting to change the design goals, BTW.

Can you provide a source reference to this decision? I can't seem to find it.

See Goals here: https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals

Can you provide a source reference to this decision? I can't seem to find it.

See Goals here: https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals

How would the multiple catch clause go against the mission goals exactly?

Also, they're aligning "with current and future ECMAScript proposals", not with the specification itself. There's a difference.

And I'm not requesting to change the design goals.

And again, I was asking for the reference to the decision to do this, as was mentioned. The commenter said there was a decision and I only would like to know where it is. :)

they will not be introducing features like this that deviate from the specification.

Are you speaking on behalf of Microsoft? Are you on the Microsoft team? Who's specification? JavaScript? Well introduce types and immediately it's against JavaScript specification.. what's your point?

In fact, I think this "aligns" with goal #1 very nicely.. "1. Statically identify constructs that are likely to be errors."

By clarifying in more detail to exactly catch the different typed errors visually and cleaner, than using many if statements in one single catch block, and safely rethrow if none are found.. just like others are most likely used to coming from java and the like.

I’ve been following the project since the beginning and I’ve seen and even started enough of these types of conversations to know where Microsoft stands on these points. I didn’t like it at first, but now I 100% get it and respect their design goals.

There are other ways to statically identify constructs that are likely to be errors without adding new JS syntax that requires not just throwing away type information at compile time, but also modifying the AST.

I think the best way to think about it is if you throw away all the type information, the JS that is left should ideally be runnable, which might also require shims, but runnable without modification to the AST.

Again, you seem to think this is against the design goals? Many changes in the language including types, change the AST (Abstract Syntax Tree). Have you ever worked on a compiler? I have (if anyone is curious I did a bit of work on the Free Pascal compiler).

I referenced what the JavaScript could would be here: https://github.com/microsoft/TypeScript/issues/20024#issuecomment-671116935

What the transpiler would emit. It's safe, simple, straight forward. Requires no shims, and does not somehow modify JavaScripts AST. :)

The first non-goal states the following:

use the behavior of JavaScript and the intentions of program authors as a guide for what makes the most sense in the language.

If this were Python, your request here would make perfect sense, as Python already supports it like so:

try:
  print(x)
except NameError:
  print("Variable x is not defined")
except:
  print("Something else went wrong")

But this is not Python.


Non-goal #5 states the following:

emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

You can see the problem pretty quickly if you try to transpile your proposal into JS. My best attempt would be the following:

  try {
    something();
  } catch (err) {
    if (err istanceof SomeError) {
      doSomethingElse();
    } else if (err instanceof AnotherError) {
      doAnotherThing();
    } else if (err instanceof YetAnotherError) {
      yetAnotherThing();
    }
  }

Notice how this now "requires runtime metadata"; furthermore, what should happen if SomeError were not a class, but an interface? What then? TS is _not_ going to be doing run-time type checking. Remember that it's not just errors that can be caught, as you can throw anything.

Again, you seem to think this is against the design goals?

It's against the non-goals, as stated above. Also, you're looking for "sugar," which is not in the design goals.

Many changes in the language including types, change the AST (Abstract Syntax Tree).

Sure, but my point was "after the types have been removed."

Have you ever worked on a compiler?

I translated the entire PostCSS project into TypeScript, but I don't see how that's relevant. I've also used the TypeScript compiler API to manipulate and create AST, so I'm very aware of how it works.

What the transpiler would emit. It's safe, simple, straight forward. Requires no shims, and does not somehow modify JavaScripts AST. :)

It's only simple in your exact use case and only if all the types are classes or functions (instanceof-able). It also modifies the AST, for sure. I'm not sure why you think that it doesn't, as it has to translate catch clauses into separate if statements with instanceof checks. You are deleting the catch clause AST nodes and inserting new ones for the if statements.

Notice how this now "requires runtime metadata"

It requires no runtime metadata, as you can see. :)

I asked if you worked on a compiler because you mentioned it modified the AST, which most real typescript language changes do? Arrow functions in fact translate to "function ()" and surrounds by an enclosure if I'm not mistaken? Either way, features like that drastically change the emitted AST.. Like most of the features when they target ES5.

Anyway, I'm done debating on this. If others would like it, they can voice.

Notice how this now "requires runtime metadata"

It requires no runtime metadata, as you can see. :)

It absolutely does. The instanceof checks are runtime checks. And there is no way to check these statements against an interface or type that is not a class or function.

I asked if you worked on a compiler because you mentioned it modified the AST, which most real typescript language changes do? Arrow functions in fact translate to "function ()" and surrounds by an enclosure if I'm not mistaken?

Only to support older targets, but you are mistaken if you are talking about compiling to a modern target. Aside from access modifiers and decorators, you should be able to compile ESNext TS into ESNext JS by simply removing the type nodes from the AST.

Have you ever worked on a compiler? I have (if anyone is curious I did a bit of work on the Free Pascal compiler).

Hey all, let's please be respectful in tone - this can come off as intimidating.

@cyraid Even if this is a syntactically-driven construct (i.e. only entities that resolve to both a value and a type are allowed in those catch clauses),

  • it is very confusing to reuse : EntityName for anything other than erasable type annotations that have no runtime behavior
  • it conflicts, or competes, with a potential future ECMAScript syntax.

On TypeScript, we intentionally aim not to add new constructs that have any sort of runtime behavior at all if they are not part of ECMAScript, and that stance has hardened over the years.

Sorry, my answer was a bit short. This discussion has exploded a bit, but let me explain my initial reasoning: Sure, they (most typescript features) are additions, but not structurally. Most annotations can be removed without transpilation. I'm a fan of terser forms of expressing a goal. But not when it changes the mental flow of the language, and might possiby collide with the development of EcmaScript itself. That's also reflected in their design goals, particularly:

  1. Avoid adding expression-level syntax.

See https://github.com/microsoft/TypeScript/issues/18500#issuecomment-329834072 as a reference.

I wonder when will this issues be implemented?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

seanzer picture seanzer  ·  3Comments

uber5001 picture uber5001  ·  3Comments

blendsdk picture blendsdk  ·  3Comments

dlaberge picture dlaberge  ·  3Comments

fwanicka picture fwanicka  ·  3Comments