      title: Metadata Proposal - ECMAScript
      status: proposal
      location: https://rbuckton.github.io/reflect-metadata
      copyright: false
      contributors: Ron Buckton

# Metadata Proposal - ECMAScript

A shim for this API can be found here: https://github.com/rbuckton/ReflectDecorators

Proposal to add Metadata to ECMAScript.

# Syntax

This section is non-normative.

        // define metadata on an object or property
        Reflect.defineMetadata(metadataKey, metadataValue, target);
        Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);

        // check for presence of a metadata key on the prototype chain of an object or property
        let result = Reflect.hasMetadata(metadataKey, target);
        let result = Reflect.hasMetadata(metadataKey, target, propertyKey);

        // check for presence of an own metadata key of an object or property
        let result = Reflect.hasOwnMetadata(metadataKey, target);
        let result = Reflect.hasOwnMetadata(metadataKey, target, propertyKey);

        // get metadata value of a metadata key on the prototype chain of an object or property
        let result = Reflect.getMetadata(metadataKey, target);
        let result = Reflect.getMetadata(metadataKey, target, propertyKey);

        // get metadata value of an own metadata key of an object or property
        let result = Reflect.getOwnMetadata(metadataKey, target);
        let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey);

        // get all metadata keys on the prototype chain of an object or property
        let result = Reflect.getMetadataKeys(target);
        let result = Reflect.getMetadataKeys(target, propertyKey);

        // get all own metadata keys of an object or property
        let result = Reflect.getOwnMetadataKeys(target);
        let result = Reflect.getOwnMetadataKeys(target, propertyKey);

        // delete metadata from an object or property
        let result = Reflect.deleteMetadata(metadataKey, target);
        let result = Reflect.deleteMetadata(metadataKey, target, propertyKey);

        // apply metadata via a decorator to a constructor
        @Reflect.metadata(metadataKey, metadataValue)
        class C {
          // apply metadata via a decorator to a method (property)
          @Reflect.metadata(metadataKey, metadataValue)
          method() {
          }
        }

        // Design-time type annotations
        function Type(type) { return Reflect.metadata("design:type", type); }
        function ParamTypes(...types) { return Reflect.metadata("design:paramtypes", types); }
        function ReturnType(type) { return Reflect.metadata("design:returntype", type); }

        // Decorator application
        @ParamTypes(String, Number)
        class C {
          constructor(text, i) {
          }

          @Type(String)
          get name() { return "text"; }

          @Type(Function)
          @ParamTypes(Number, Number)
          @ReturnType(Number)
          add(x, y) {
            return x + y;
          }
        }

        // Metadata introspection
        let obj = new C("a", 1);
        let paramTypes = Reflect.getMetadata("design:paramtypes", inst, "add"); // [Number, Number]

# Abstract Operations

# Operations on Objects

# GetOrCreateMetadataMap ( O, P, Create )

When the abstract operation GetOrCreateMetadataMap is called with Object _O_, property key _P_, and Boolean _Create_ the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _targetMetadata_ be the value of _O_'s [[Metadata]] internal slot. 3. If _targetMetadata_ is *undefined*, then 1. If _Create_ is *false*, return *undefined*. 2. Set _targetMetadata_ to be a newly created *Map* object. 3. Set the [[Metadata]] internal slot of _O_ to _targetMetadata_. 4. Let _metadataMap_ be ? Invoke(_targetMetadata_, `"get"`, _P_). 5. If _metadataMap_ is *undefined*, then 1. If _Create_ is *false*, return *undefined*. 2. Set _metadataMap_ to be a newly created *Map* object. 3. Perform ? Invoke(_targetMetadata_, `"set"`, _P_, _metadataMap_). 6. Return _metadataMap_.

# Ordinary and Exotic Objects Behaviors

# Ordinary Object Internal Methods and Internal Slots

All ordinary objects have an internal slot called [[Metadata]]. The value of this internal slot is either *null* or a *Map* object and is used for storing metadata for an object.

# [[HasMetadata]] ( MetadataKey, P )

When the [[HasMetadata]] internal method of _O_ is called with ECMAScript language value _MetadataKey_ and property key _P_, the following steps are taken:

1. Return ? OrdinaryHasMetadata(_MetadataKey_, _O_, _P_).

# OrdinaryHasMetadata ( MetadataKey, O, P )

When the abstract operation OrdinaryHasMetadata is called with ECMAScript language value _MetadataKey_, Object _O_, and property key _P_, the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _hasOwn_ be ? OrdinaryHasOwnMetadata(_MetadataKey_, _O_, _P_). 3. If _hasOwn_ is *true*, return *true*. 4. Let _parent_ be ? _O_.[[GetPrototypeOf]](). 5. If _parent_ is not *null*, Return ? _parent_.[[HasMetadata]](_MetadataKey_, _P_). 6. Return *false*.

# [[HasOwnMetadata]] ( MetadataKey, P )

When the [[HasOwnMetadata]] internal method of _O_ is called with ECMAScript language value _MetadataKey_ and property key _P_, the following steps are taken:

1. Return ? OrdinaryHasOwnMetadata(_MetadataKey_, _O_, _P_).

# OrdinaryHasOwnMetadata ( MetadataKey, O, P )

When the abstract operation OrdinaryHasOwnMetadata is called with ECMAScript language value _MetadataKey_, Object _O_, and property key _P_, the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _metadataMap_ be ? GetOrCreateMetadataMap(_O_, _P_, *false*). 3. If _metadataMap_ is *undefined*, return *false*. 4. Return ? ToBoolean(? Invoke(_metadataMap_, `"has"`, _MetadataKey_)).

# [[GetMetadata]] ( MetadataKey, P )

When the [[GetMatadata]] internal method of _O_ is called with ECMAScript language value _MetadataKey_ and property key _P_, the following steps are taken:

1. Return ? OrdinaryGetMetadata(_MetadataKey_, _O_, _P_).

# OrdinaryGetMetadata ( MetadataKey, O, P )

When the abstract operation OrdinaryGetMetadata is called with ECMAScript language value _MetadataKey_, Object _O_, and property key _P_, the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _hasOwn_ be ? OrdinaryHasOwnMetadata(_MetadataKey_, _O_, _P_). 3. If _hasOwn_ is *true*, return ? OrdinaryGetOwnMetadata(_MetadataKey_, _O_, _P_). 4. Let _parent_ be ? _O_.[[GetPrototypeOf]](). 5. If _parent_ is not *null*, return ? _parent_.[[GetMetadata]](_MetadataKey_, _P_). 6. Return *undefined*.

# [[GetOwnMetadata]] ( MetadataKey, P, ParamIndex )

When the [[GetOwnMetadata]] internal method of _O_ is called with ECMAScript language value _MetadataKey_ and property key _P_, the following steps are taken:

1. Return ? OrdinaryGetOwnMetadata(_MetadataKey_, _O_, _P_).

# OrdinaryGetOwnMetadata ( MetadataKey, O, P )

When the abstract operation OrdinaryGetOwnMetadata is called with ECMAScript language value _MetadataKey_, Object _O_, and property key _P_, the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _metadataMap_ be ? GetOrCreateMetadataMap(_O_, _P_, *false*). 3. If _metadataMap_ is *undefined*, return *undefined*. 4. Return ? Invoke(_metadataMap_, `"get"`, _MetadataKey_).

# [[DefineOwnMetadata]] ( MetadataKey, MetadataValue, P )

When the [[DefineOwnMetadata]] internal method of _O_ is called with ECMAScript language value _MetadataKey_, ECMAScript language value _MetadataValue_, and property key _P_, the following steps are taken:

1. Return ? OrdinaryDefineOwnMetadata(_MetadataKey_, _MetadataValue_, _O_, _P_)

# OrdinaryDefineOwnMetadata ( MetadataKey, MetadataValue, O, P )

When the abstract operation OrdinaryDefineOwnProperty is called with ECMAScript language value _MetadataKey_, ECMAScript language value _MetadataValue_, Object _O_, and property key _P_, the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _metadataMap_ be ? GetOrCreateMetadataMap(_O_, _P_, *true*). 3. Return ? Invoke(_metadataMap_, `"set"`, _MetadataKey_, _MetadataValue_).

# [[MetadataKeys]] ( P )

When the [[MetadataKeys]] internal method of _O_ is called with property key _P_ the following steps are taken:

1. Return ? OrdinaryMetadataKeys(_O_, _P_).

# OrdinaryMetadataKeys ( O, P )

When the abstract operation OrdinaryMetadataKeys is called with Object _O_ and property key _P_ the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _ownKeys_ be ? OrdinaryOwnMetadataKeys(_O_, _P_). 3. Let _parent_ be ? _O_.[[GetPrototypeOf]](). 4. If _parent_ is *null*, then return _ownKeys_. 5. Let _parentKeys_ be ? _O_.[[OrdinaryMetadataKeys]](_P_). 6. Let _ownKeysLen_ = ? Get(_ownKeys_, "length"). 7. If _ownKeysLen_ is *0*, return _parentKeys_. 8. Let _parentKeysLen_ = ? Get(_parentKeys_, "length"). 9. If _parentKeysLen_ is *0*, return _ownKeys_. 10. Let _set_ be a newly created *Set* object. 11. Let _keys_ be ? ArrayCreate(0). 12. Let _k_ be *0*. 13. For each element _key_ of _ownKeys_ 1. Let _hasKey_ be ? Invoke(_set_, `"has"`, _key_). 2. If _hasKey_ is *false*, then 1. Let _Pk_ be ! ToString(_k_). 2. Perform ? Invoke(_set_, "add", _key_). 3. Let _defineStatus_ be CreateDataProperty(_keys_, _Pk_, _key_). 4. Assert: _defineStatus_ is *true*. 5. Increase _k_ by *1*. 14. For each element _key_ of _parentKeys_ 1. Let _hasKey_ be ? Invoke(_set_, `"has"`, _key_). 2. If _hasKey_ is *false*, then 1. Let _Pk_ be ! ToString(_k_). 2. Perform ? Invoke(_set_, `"add"`, _key_). 3. Let _defineStatus_ be CreateDataProperty(_keys_, _Pk_, _key_). 4. Assert: _defineStatus_ is *true*. 5. Increase _k_ by *1*. 15. Perform ? Set(_keys_, `"length"`, _k_). 16. return _keys_.

# [[OwnMetadataKeys]] ( P )

When the [[OwnMetadataKeys]] internal method of _O_ is called with property key _P_ the following steps are taken:

1. Return OrdinaryOwnMetadataKeys(_O_, _P_).

# OrdinaryOwnMetadataKeys ( O, P )

When the abstract operation OrdinaryOwnMetadataKeys is called with Object _O_ and property key _P_ the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _keys_ be ? ArrayCreate(0). 3. Let _metadataMap_ be ? GetOrCreateMetadataMap(_O_, _P_, *false*). 4. If _metadataMap_ is *undefined*, return _keys_. 5. Let _keysObj_ be ? Invoke(_metadataMap_, `"keys"`). 6. Let _iterator_ be ? GetIterator(_keysObj_). 7. Let _k_ be *0*. 8. Repeat 1. Let _Pk_ be ! ToString(_k_). 2. Let _next_ be ? IteratorStep(_iterator_). 3. If _next_ is *false*, then 1. Let _setStatus_ be ? Set(_keys_, `"length"`, _k_, _true_). 2. Assert: _setStatus_ is *true*. 3. Return _keys_. 4. Let _nextValue_ be ? IteratorValue(_next_). 5. Let _defineStatus_ be CreateDataPropertyOrThrow(_keys_, _Pk_, _nextValue_). 6. If _defineStatus_ is an abrupt completion, return ? IteratorClose(_iterator_, _defineStatus_). 7. Increase _k_ by *1*.

# [[DeleteMetadata]]( MetadataKey, P )

When the [[DeleteMetadata]] internal method of _O_ is called with ECMAScript language value _MetadataKey_ and property key _P_ the following steps are taken:

1. Assert: _P_ is *undefined* or IsPropertyKey(_P_) is *true*. 2. Let _metadataMap_ be ? GetOrCreateMetadataMap(_O_, _P_, *false*). 4. If _metadataMap_ is *undefined*, return *false*. 5. Return ? Invoke(_metadataMap_, `"delete"`, _MetadataKey_).

# Reflection

# The Reflect Object

This section contains amendments to the Reflect object.

# Metadata Decorator Functions

A metadata decorator function is an anonymous built-in function that has [[MetadataKey]] and [[MetadataValue]] internal slots.

When a metadata decorator function _F_ is called with arguments _target_ and _key_, the following steps are taken:

1. Assert: _F_ has a [[MetadataKey]] internal slot whose value is an ECMAScript language value, or *undefined*. 2. Assert: _F_ has a [[MetadataValue]] internal slot whose value is an ECMAScript language value, or *undefined*. 3. If Type(_target_) is not Object, throw a *TypeError* exception. 4. If _key_ is not *undefined* and IsPropertyKey(_key_) is *false*, throw a *TypeError* exception. 5. Let _metadataKey_ be the value of _F_'s [[MetadataKey]] internal slot. 6. Let _metadataValue_ be the value of _F_'s [[MetadataValue]] internal slot. 7. Perform ? _target_.[[DefineMetadata]](_metadataKey_, _metadataValue_, _target_, _key_). 8. Return *undefined*.

# Reflect.metadata ( metadataKey, metadataValue )

When the `metadata` function is called with arguments _metadataKey_ and _metadataValue_, the following steps are taken:

1. Let _decorator_ be a new built-in function object as defined in Metadata Decorator Functions. 2. Set the [[MetadataKey]] internal slot of _decorator_ to _metadataKey_. 3. Set the [[MetadataValue]] internal slot of _decorator_ to _metadataValue_. 4. Return _decorator_.

# Reflect.defineMetadata ( metadataKey, metadataValue, target [, propertyKey] )

When the `defineMetadata` function is called with arguments _metadataKey_, _metadataValue_, _target_, and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[DefineMetadata]](_metadataKey_, _metadataValue_, _propertyKey_).

# Reflect.hasMetadata ( metadataKey, target [, propertyKey] )

When the `hasMetadata` function is called with arguments _metadataKey_, _target_, and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[HasMetadata]](_metadataKey_, _propertyKey_).

# Reflect.hasOwnMetadata ( metadataKey, target [, propertyKey] )

When the `hasOwnMetadata` function is called with arguments _metadataKey_, _target_, and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[HasOwn]](_metadataKey_, _propertyKey_).

# Reflect.getMetadata ( metadataKey, target [, propertyKey] )

When the `getMetadata` function is called with arguments _metadataKey_, _target_, and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[GetMetadata]](_metadataKey_, _propertyKey_).

# Reflect.getOwnMetadata ( metadataKey, target [, propertyKey] )

When the `getOwnMetadata` function is called with arguments _metadataKey_, _target_, and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[GetOwnMetadata]](_metadataKey_, _propertyKey_).

# Reflect.getMetadataKeys ( target [, propertyKey] )

When the `getMetadataKeys` function is called with arguments _target_ and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[GetMetadataKeys]](_propertyKey_).

# Reflect.getOwnMetadataKeys ( target [, propertyKey] )

When the `getOwnMetadataKeys` function is called with arguments _target_ and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[GetOwnMetadataKeys]](_propertyKey_).

# Reflect.deleteMetadata ( metadataKey, target [, propertyKey] )

When the `deleteMetadata` function is called with arguments _metadataKey_, _target_, and _propertyKey_, the following steps are taken:

1. If Type(_target_) is not Object, throw a *TypeError* exception. 2. Return ? _target_.[[DeleteMetadata]](_metadataKey_, _propertyKey_).