https://github.com/tc39/proposal-decorator-metadata
https://github.com/tc39/proposal-decorator-metadata
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/tc39/proposal-decorator-metadata
- Owner: tc39
- License: mit
- Created: 2022-04-01T16:36:27.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2024-01-17T17:00:32.000Z (almost 2 years ago)
- Last Synced: 2025-08-15T09:58:49.455Z (5 months ago)
- Language: HTML
- Homepage:
- Size: 85.9 KB
- Stars: 171
- Watchers: 43
- Forks: 9
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Decorator Metadata
**Stage**: 3
**Spec Text**: https://github.com/pzuraq/ecma262/pull/10
This proposal seeks to extend the [Decorators](https://github.com/tc39/proposal-decorators)
proposal by adding the ability for decorators to associate _metadata_ with the
value being decorated.
## Overview
Decorators are functions that allow users to metaprogram by wrapping and
replacing existing values. This allows them to solve a number of use cases quite
well, such as memoization, reactivity, method binding, and more. However, there
are a number of use cases which require code _external_ to the decorator and
decorated class to be able to introspect and understand what decorations were
applied, including:
- Validation
- Serialization
- Web component definition
- Dependency injection
- Declarative routing/application structure
- ...and more
In previous iterations of the decorators proposal, all decorators had access to
the class prototype, allowing them to associate metadata directly via a
`WeakMap` by using the class as a key. This is no longer possible in the most
recent version, however, as decorators only have access to the value they are
_directly_ decorating (e.g. method decorators have access to the method, field
decorators have access to the field, etc).
This proposal extends decorators by providing a metadata _object_, which can be
used either to directly store metadata, or as a WeakMap key. This object is
provided via the decorator's context argument, and is then accessible via the
`Symbol.metadata` property on the class definition after decoration.
## Detailed Design
The overall decorator signature will be updated to the following:
```ts
type Decorator = (value: Input, context: {
kind: string;
name: string | symbol;
access: {
get?(): unknown;
set?(value: unknown): void;
};
isPrivate?: boolean;
isStatic?: boolean;
addInitializer?(initializer: () => void): void;
+ metadata?: Record;
}) => Output | void;
```
The new `metadata` property is a plain JavaScript object. The same object is
passed to every decorator applied to a class or any of its elements. After the
class has been fully defined, it is assigned to the `Symbol.metadata` property
of the class.
An example usage might look like:
```js
function meta(key, value) {
return (_, context) => {
context.metadata[key] = value;
};
}
@meta('a', 'x')
class C {
@meta('b', 'y')
m() {}
}
C[Symbol.metadata].a; // 'x'
C[Symbol.metadata].b; // 'y'
```
### Inheritance
If the decorated class has a parent class, then the prototype of the `metadata`
object is set to the metadata object of the superclass. This allows metadata to
be inherited in a natural way, taking advantage of shadowing by default,
mirroring class inheritance. For example:
```js
function meta(key, value) {
return (_, context) => {
context.metadata[key] = value;
};
}
@meta('a', 'x')
class C {
@meta('b', 'y')
m() {}
}
C[Symbol.metadata].a; // 'x'
C[Symbol.metadata].b; // 'y'
class D extends C {
@meta('b', 'z')
m() {}
}
D[Symbol.metadata].a; // 'x'
D[Symbol.metadata].b; // 'z'
```
In addition, metadata from the parent can be read during decoration, so it can
be modified or extended by children rather than overriding it.
```ts
function appendMeta(key, value) {
return (_, context) => {
// NOTE: be sure to copy, not mutate
const existing = context.metadata[key] ?? [];
context.metadata[key] = [...existing, value];
};
}
@appendMeta('a', 'x')
class C {}
@appendMeta('a', 'z')
class D extends C {}
C[Symbol.metadata].a; // ['x']
D[Symbol.metadata].a; // ['x', 'z']
```
### Private Metadata
In addition to public metadata placed directly on the metadata object, the
object can be used as a key in a `WeakMap` if the decorator author does not want
to share their metadata.
```ts
const PRIVATE_METADATA = new WeakMap();
function meta(key, value) {
return (_, context) => {
let metadata = PRIVATE_METADATA.get(context.metadata);
if (!metadata) {
metadata = {};
PRIVATE_METADATA.set(context.metadata, metadata);
}
metadata[key] = value;
};
}
@meta('a', 'x')
class C {
@meta('b', 'y')
m() {}
}
PRIVATE_METADATA.get(C[Symbol.metadata]).a; // 'x'
PRIVATE_METADATA.get(C[Symbol.metadata]).b; // 'y'
```