You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Implement the updated JS decorators proposal #48885
List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback.
Decorators
✅ Viability Checklist
My suggestion meets these guidelines:
This wouldn't be a breaking change in existing TypeScript/JavaScript code
This wouldn't change the runtime behavior of existing JavaScript code
This could be implemented without emitting different JS based on the types of the expressions
This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
but now that the proposal is moving forward, currently at Stage 3, I thought I'd open this issue to see if there are plans to finally, fully implement (and type) the ES version of decorators.
📃 Motivating Example
N/A - just implementing new JS features, to keep JS and TS aligned.
I think the first step will be implementing decorators. We're not going to try to model the "decorators can change the types of things" portion at first, but I think we can get something good and working.
Reacted by Adam Rackis, Vlad Dumbrava, Glen, Cry0nicS, Craig Johnson, Chris Krycho, ExE Boss, Nico Jansen, Andrew Stegmaier, Esdras Amora and 20 more
Reacted by Adam Rackis, Roger Padilla, lin72h, Richard Simpson, Kirill Groshkov and Thanh Vu
Reacted by FUJI Goro, Jack Works, Esdras Amora, Roger Padilla, lin72h, Richard Simpson, Kirill Groshkov, Gabriele Tomberli and Salathiel Genese
Reacted by Adam Rackis, Daniel Rosenwasser, Tom Marius, Chris Krycho, Toni Villena, ExE Boss, Ar4ys, Jacob Richter, Jan Sedloň, Jingkun Hua and 45 more
Reacted by Ricardo Fernández Serrata and Salathiel Genese
Daniel Rosenwasser (@DanielRosenwasser) the biggest "type-related" transformation that comes up for me in the current implementation of decorators is communicating that a field decorator initializes the field's value.
I think that this would just fall out of accessor decorators (since the decorator turns into a getter, the concept of an uninitialized value no longer makes sense). Is that right?
Also, a regular field decorator might still want to communicate that the value is initialized (for example, a decorator that implements a default by returning a new initializer). Is it possible to address the issue of communicating that a decorator initializes the value on a different timeframe than arbitrary type transformation, or is it just as hard?
We're not going to try to model the "decorators can change the types of things" portion at first, but I think we can get something good and working.
Simple transform decorators should be easy enough to support right?
Like:
functionwrapInBox<R>(f: () => R): ()=>{value: R}{return()=>{value: f()};}classFoo{
@wrapInBoxmethod(): number{return3;}}constfoo=newFoo();foo.method();// should be { value: number }, same as regular function wrapping
Has very little difference to (type inference wise):
Also, a regular field decorator might still want to communicate that the value is initialized (for example, a decorator that implements a default by returning a new initializer). Is it possible to address the issue of communicating that a decorator initializes the value on a different timeframe than arbitrary type transformation, or is it just as hard?
Do you mean something like this?
classFoo{
@alwaysStringfoo=123// initializes it to "123"}
I think TypeScript would create the type right then and there, at class definition. The user cannot (write any code that will)
observe a moment when the value is a number instead of a string, apart from the decorators themselves.
Another thing could be that maybe decorators can augment the type of a class, but not necessarily the type of the thing they decorate directly. For example, this
classFoo{
@withDoublecount=123}
could add a new doubleCount variable that is always the double of count whenever count changes. The type would be { count: number, doubleCount: number } where count was not modified.
However, I think that with this new decorator API, decorator functions can be completely generic, with their input and output types completely known by TypeScript. So, this should be possible:
functionalwaysString<T,Cextends ...>(_: T,context: C): (initial: T)=>string{if(context.kind!=='field')return// type narrowing based on union of string values for `kind`returninitial=>initial.toString()}
and based on something like this, I imagine it totally possible for TypeScript to look at the return type and determine that the final type of the property should always be a string (or to always have any setter, string getter).
But a version with class types not modifiable would still be totally usable!
I suggest implement decorators first, then add type support. The current legacy class decorator could return a non-class constructor value, and it won't be recognized by ts type system, but it is still easy to use.
Or we just use legacy version until stage 4 published, but maybe they are waiting us to do something to verify their design in stage 3.
Sorry for the offtopic question, but what will happen to stage3 decorators? Will they be removed from Typescript?
I assume you mean the Stage 1 "experimental" decorators that are already in TypeScript? That will remain behind --experimentalDecorators for the foreseeable future as there are a number of decorator features that are not yet covered by the current Decorators proposal:
We also do not currently support decorated declare fields with Stage 3 decorators, as that would introduce significant additional complexity due to the timing of decorator application.
Ron Buckton (@rbuckton), thank you, I mean ES decorators reached stage 3. And because of that, I believe they'll be implemented in the Typescript as well. And correct me if I'm wrong, but the newest decorators are not compatible with the good old "experimental". So I thought, Typescript will have to support them since a lot of frameworks are relying on them.
In other words, my question is "should I write a new code with the experimental decorators or will they be deprecated in about 3 years?"
Thanks in advance!
Danilian Akhmedzianov (@pokatomnik) while experimental stage 1 decorators will be supported for some unknown amount of time, I believe we can assume that eventually non-experimental stage 3 decorators will land and we'll be able to rely on those.
If you want to prepare for this future, the best way to do that currently is to use tsc only for type checking, and use babel for transpiling stage 3 decorators today. Then it should be minimal effort later to switch to using just tsc for type checking and compiling.
I'm betting on stage 3 decorators with a setup like I mentioned in the above comment.
Hope there is adapter to update experimental decorators to stage 3 automatic.
I don't think TypeScript will do that (I don't think it has ever been done for any feature). Experimental stage 1 decorators and stage 3 decorators are highly incompatible. Start migrating!
Hope there is adapter to update experimental decorators to stage 3 automatic.
I don't think TypeScript will do that (I don't think it has ever been done for any feature). Experimental stage 1 decorators and stage 3 decorators are highly incompatible. Start migrating!
I will try, but it is a terrible work especially for package creators to update their user's code.
Hope there is adapter to update experimental decorators to stage 3 automatic.
I don't think TypeScript will do that (I don't think it has ever been done for any feature). Experimental stage 1 decorators and stage 3 decorators are highly incompatible. Start migrating!
No, we won't automatically adapt legacy decorators as that would require type-directed emit. Also, there is not 1:1 parity between legacy decorators and ECMAScript decorators, which I mentioned above.
It's possible to write a custom decorator adapter, though that would entail varying degrees of complexity depending on the decorator you are adapting. An adapter that provided full backwards compatibility with legacy decorators would likely incur a performance penalty, though it would be feasible:
// legacy.ts
@foo
@bar({x: 1})classC{
@bazmethod(){}
@quxxfield;}// native.tsimport{createDecoratorAdapter}from"./adapter";const_=createDecoratorAdapter();
@_// <- last applied decorator
@_(foo)
@_(bar({x: 1})classC{
@_(baz)method(){}
@_(quxx)field;}// adapter.tsexportfunctioncreateDecoratorAdapter(){// TODO: returns a function that queues up each legacy decorator application// and performs legacy decorator evaluation at the end as part of a// final class decorator.}
NOTE: It's not designed to be intermingled with native decorators, as all decorator applications are queued until an adapted class decorator runs (or the adapter itself is used as a class decorator).
Activity
DanielRosenwasser commentedon Apr 29, 2022
I think the first step will be implementing decorators. We're not going to try to model the "decorators can change the types of things" portion at first, but I think we can get something good and working.
arackaf commentedon Apr 30, 2022
Daniel Rosenwasser (@DanielRosenwasser) that sounds perfect.
But just to be clear, annotating how a decorator change’s a type will be on the roadmap long term?
glen-84 commentedon May 1, 2022
Shhh, don't say "long term". 😉
wycats commentedon May 13, 2022
Daniel Rosenwasser (@DanielRosenwasser) the biggest "type-related" transformation that comes up for me in the current implementation of decorators is communicating that a field decorator initializes the field's value.
I think that this would just fall out of
accessordecorators (since the decorator turns into a getter, the concept of an uninitialized value no longer makes sense). Is that right?Also, a regular field decorator might still want to communicate that the value is initialized (for example, a decorator that implements a default by returning a new initializer). Is it possible to address the issue of communicating that a decorator initializes the value on a different timeframe than arbitrary type transformation, or is it just as hard?
Jamesernator commentedon May 14, 2022
Simple transform decorators should be easy enough to support right?
Like:
Has very little difference to (type inference wise):
(Ignoring the prototype vs own property distinction here).
Isn't it primarily the metadata-based type-mutations that are hard to actually support? (given they need to communicate types across calls)
trusktr commentedon Aug 17, 2022
Besides this, also picking types from a class by specific decorator would be wonderful... in the longer term. :D
trusktr commentedon Aug 18, 2022
Do you mean something like this?
I think TypeScript would create the type right then and there, at class definition. The user cannot (write any code that will)
observe a moment when the value is a number instead of a string, apart from the decorators themselves.
Another thing could be that maybe decorators can augment the type of a class, but not necessarily the type of the thing they decorate directly. For example, this
could add a new
doubleCountvariable that is always the double ofcountwhenevercountchanges. The type would be{ count: number, doubleCount: number }wherecountwas not modified.However, I think that with this new decorator API, decorator functions can be completely generic, with their input and output types completely known by TypeScript. So, this should be possible:
and based on something like this, I imagine it totally possible for TypeScript to look at the return type and determine that the final type of the property should always be a
string(or to always have any setter, string getter).But a version with class types not modifiable would still be totally usable!
ruojianll commentedon Aug 19, 2022
I suggest implement decorators first, then add type support. The current legacy class decorator could return a non-class constructor value, and it won't be recognized by ts type system, but it is still easy to use.
Or we just use legacy version until stage 4 published, but maybe they are waiting us to do something to verify their design in stage 3.
wycats commentedon Aug 25, 2022
As long as
@dec accessor foounderstands that you don't need to explicitly initializefoo, I think I can live with that.14 remaining items
pokatomnik commentedon Nov 9, 2022
Sorry for the offtopic question, but what will happen to stage3 decorators? Will they be removed from Typescript?
rbuckton commentedon Nov 9, 2022
I assume you mean the Stage 1 "experimental" decorators that are already in TypeScript? That will remain behind
--experimentalDecoratorsfor the foreseeable future as there are a number of decorator features that are not yet covered by the current Decorators proposal:{ get, set }(https://github.com/tc39/proposal-grouped-and-auto-accessors). While this does exist for the newaccessorfields, there is no easy alternative for normal getters and setters.We also do not currently support decorated
declarefields with Stage 3 decorators, as that would introduce significant additional complexity due to the timing of decorator application.rbuckton commentedon Nov 9, 2022
The built-in lib definitions in #50820 contain types that will hopefully be able to help:
pokatomnik commentedon Nov 9, 2022
Ron Buckton (@rbuckton), thank you, I mean ES decorators reached stage 3. And because of that, I believe they'll be implemented in the Typescript as well. And correct me if I'm wrong, but the newest decorators are not compatible with the good old "experimental". So I thought, Typescript will have to support them since a lot of frameworks are relying on them.
In other words, my question is "should I write a new code with the experimental decorators or will they be deprecated in about 3 years?"
Thanks in advance!
ruojianll commentedon Nov 10, 2022
Hope there is adapter to update experimental decorators to stage 3 automatic.
trusktr commentedon Nov 12, 2022
Danilian Akhmedzianov (@pokatomnik) while experimental stage 1 decorators will be supported for some unknown amount of time, I believe we can assume that eventually non-experimental stage 3 decorators will land and we'll be able to rely on those.
If you want to prepare for this future, the best way to do that currently is to use
tsconly for type checking, and usebabelfor transpiling stage 3 decorators today. Then it should be minimal effort later to switch to using justtscfor type checking and compiling.I'm betting on stage 3 decorators with a setup like I mentioned in the above comment.
trusktr commentedon Nov 12, 2022
I don't think TypeScript will do that (I don't think it has ever been done for any feature). Experimental stage 1 decorators and stage 3 decorators are highly incompatible. Start migrating!
ruojianll commentedon Nov 15, 2022
I will try, but it is a terrible work especially for package creators to update their user's code.
jithujoshyjy commentedon Nov 15, 2022
A little nod about the metadata in decorators. tc39/proposal-type-annotations#159 can this be done in typescript?
rbuckton commentedon Nov 15, 2022
No, we won't automatically adapt legacy decorators as that would require type-directed emit. Also, there is not 1:1 parity between legacy decorators and ECMAScript decorators, which I mentioned above.
It's possible to write a custom decorator adapter, though that would entail varying degrees of complexity depending on the decorator you are adapting. An adapter that provided full backwards compatibility with legacy decorators would likely incur a performance penalty, though it would be feasible:
rbuckton commentedon Nov 15, 2022
Here's a rough implementation of a comprehensive
createDecoratorAdapter(): https://gist.github.com/rbuckton/a464d1a0997bd3dab36c8b0caef0959aNOTE: It's not designed to be intermingled with native decorators, as all decorator applications are queued until an adapted class decorator runs (or the adapter itself is used as a class decorator).