# Metadata Proposal - ECMAScript

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

Proposal to add Metadata to ECMAScript.

# 1Syntax

Note
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]

# 2Abstract Operations

# 2.1Operations on Objects

# 2.1.1GetOrCreateMetadataMap ( 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.

# 3Ordinary and Exotic Objects Behaviors

# 3.1Ordinary 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.

# 3.1.1[[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).

# 3.1.1.1OrdinaryHasMetadata ( 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.

# 3.1.2[[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).

# 3.1.2.1OrdinaryHasOwnMetadata ( 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)).

# 3.1.3[[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).

# 3.1.3.1OrdinaryGetMetadata ( 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.

# 3.1.4[[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).

# 3.1.4.1OrdinaryGetOwnMetadata ( 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).

# 3.1.5[[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)

# 3.1.5.1OrdinaryDefineOwnMetadata ( 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).

# 3.1.6[[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).

# 3.1.6.1OrdinaryMetadataKeys ( 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.

# 3.1.7[[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).

# 3.1.7.1OrdinaryOwnMetadataKeys ( 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.

# 3.1.8[[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).
3. If metadataMap is undefined, return false.
4. Return ? Invoke(metadataMap, `"delete"`, MetadataKey).

# 4Reflection

# 4.1The Reflect Object

This section contains amendments to the Reflect object.

# 4.1.1Metadata 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.

# 4.1.2Reflect.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.

# 4.1.3Reflect.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).

# 4.1.4Reflect.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).

# 4.1.5Reflect.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).

# 4.1.6Reflect.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).

# 4.1.7Reflect.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).

# 4.1.8Reflect.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).

# 4.1.9Reflect.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).

# 4.1.10Reflect.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).