Repository navigation
Implement decorator metadata proposalΒ #53461
Description
Suggestion
Implement decorator metadata
π Search Terms
decorator metadata
β 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.)
- This feature would agree with the rest of TypeScript's Design Goals.
β Suggestion
Now that decorators are shipping in TypeScript and decorator metadata has reached consensus on the approach needed for it to proceed to Stage 3, it seems like it may be time to implement metadata.
π Motivating Example
There are examples on the metadata repo, but a quick one is storing information about a class member to be used by the class's base class. Here we generate a custom element's observedAttributes by decorating fields:
// Library code:
const attributes = new WeakMap<typeof BaseElement, Array<string>>();
class BaseElement {
static get observedAttributes() {
// real code would get attributes from super-classes too
return attributes.get(this[Symbol.metadata]);
}
}
const attribute = (_, {name, metadata}) => {
attributes.set(metadata, (attributes.get(metadata) ?? []).push(name));
}
// Usage:
class MyElement extends BaseElement {
@attribute
foo?: string;
}π» Use Cases
There are many use cases in the various decorators repositories and documents. Generally any time that some other API besides the decorated one needs information about the decorated items, you will need metadata.
It's worth noting that many libraries can't use decorators until metadata is implemented.
Activity
MartinJohns commentedon Mar 23, 2023
I don't know the current position of the team on this, but it does violate non-goal 5. The current experimental metadata support comes from the early dark times before this design goal document.Nevermind, you refer to an actual ES proposal. But that is only stage 2, usually the team waits for at least stage 3.
justinfagnani commentedon Mar 23, 2023
Martin Johns (@MartinJohns)
Yes, but like I mentioned the metadata proposal just got consensus on the approach to enable it to go to Stage 3 very soon. An issue like this can be used to track some work even before that work could be done or shipped.
DanielRosenwasser commentedon Mar 23, 2023
Tentatively scheduling for TypeScript 5.2. Luckily, that's not too far off anyway.
EisenbergEffect commentedon Mar 30, 2023
Please. Please. Please. Please. Please. Please. Please.
Daniel Rosenwasser (@DanielRosenwasser) I will send your team a cake. Or, if you like other tasties, let me know. Clearly I'm not above bribery when it comes to decorator metadata π€£
/cc Chris Holt (@chrisdholt)
EisenbergEffect commentedon May 18, 2023
Stage 3 is official!!! π
rbuckton commentedon May 19, 2023
The downlevel emit for this is fairly simple, and should work with the existing helpers. When I wrote the
__esDecorate, the intent was for it to easily support metadata a well.given
we used to emit
But in the future, we will emit this instead:
Metadata availability will be conditional on the presence of
Symbol.metadata, so as not to break existing users.EisenbergEffect commentedon Jun 23, 2023
YES!!!! Awesome work everyone. Daniel Rosenwasser (@DanielRosenwasser) please reach out to me on twitter, linkedin, etc. I really would like to thank the team somewhow.
justinfagnani commentedon Jun 26, 2023
Ron Buckton (@rbuckton) would #54657 be in the latest nightly? I'm trying to use metadata, and while it's in the typings, it appears to always be
undefined: Playgroundedit to add: I don't see a way to choose libs in the playground, but I did add
'esnext.decorators'locally, thinking that that might add the definition forSymbol.metadata, butcontext.metadatais still undefined.rbuckton commentedon Jun 26, 2023
Have you polyfilled
Symbol.metadata? TypeScript does not polyfill runtime features, so you may need to add something like the following to use it:Also, you can use
// @lib: dom,esnextin the playground to choose different libs.justinfagnani commentedon Jun 26, 2023
I just figured that out, thanks!
jakearchibald commentedon Sep 27, 2023
Ron Buckton (@rbuckton) I got caught out by
metadatabeingundefinedtoo. Would it make more sense to leave it defined even ifSymbol.metadatais missing?The current developer experience is:
context.metadata[key]context.metadataisundefined, wtf??Whereas if
context.metadatawas still defined, it's:context.metadata[key]Symbol.metadataisundefined, wtf??With the latter case, you run into the actual problem, not a side-effect of the problem.
justinfagnani commentedon Sep 27, 2023
I agree that seeing an error that
Symbol.metadatais undefined would be a lot more clear - I initially thought that the TS metadata implementation had a bug).I think changing the metadata initialization from this:
to this:
Would ensure that
context.metadatais defined, that it's shared across all decorator applications for a class, that metadata usage that's only within decorators (ie, as a way of sharing) just works, and that the error fromSymbol.metadatabeing undefined is more understandable.jakearchibald commentedon Sep 28, 2023
Yep, +1 to that implementation
rbuckton commentedon Sep 28, 2023
If
context.metadatais defined but isn't actually assigned to the class, it becomes more difficult for a decorator to test whether it can actually write metadata. This change wouldn't actually improve any use case, as any metadata you would attach would be lost.Having
context.metadatabeundefinedwhen not using a lib that has the symbol defined allows you to code defensively against environments that haven't shimmedSymbol.metadata. I'm not certain I'd be comfortable with this change.I could see adding a documentation comment to the
metadataproperty to explain the reason why it is missing.jakearchibald commentedon Sep 28, 2023
I guess if browsers shipped decorators without metadata, then
context.metadatawould be undefined.Yeah, improved documentation seems reasonable.
rbuckton commentedon Sep 28, 2023
The current approach also allows you to write the following shim in your code to enable
metadatadownlevel, if necessary:The above code would also remove the
| undefinedfromcontext.metadatadue to the conditional type used to define it.justinfagnani commentedon Oct 2, 2023
This is a really good point. We already do this in Lit 3.0 with a dev-mode warning when metadata is undefined telling people that they need to upgrade their compiler.