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_).