Prototype for a Metadata Reflection API for ECMAScript
Installations
npm install reflect-metadata
Releases
v0.2.1
Published on 14 Dec 2023
reflect-metadata 0.2.0
Published on 13 Dec 2023
reflect-metadata 0.2.0-pre.0
Published on 13 Dec 2023
reflect-metadata 0.1.14
Published on 13 Dec 2023
reflect-metadata 0.1.13
Published on 13 Dec 2023
reflect-metadata v0.1.12
Published on 16 Jan 2018
Developer
Developer Guide
Module System
CommonJS, ESM
Min. Node Version
Typescript Support
Yes
Node Version
10.22.0
NPM Version
6.14.6
Statistics
3,213 Stars
134 Commits
185 Forks
40 Watching
7 Branches
7 Contributors
Updated on 27 Nov 2024
Languages
TypeScript (92.3%)
HTML (6.91%)
JavaScript (0.79%)
Total Downloads
Cumulative downloads
Total Downloads
1,565,438,015
Last day
-1.2%
2,819,612
Compared to previous day
Last week
3.9%
14,934,721
Compared to previous week
Last month
15.7%
60,834,849
Compared to previous month
Last year
58.2%
551,983,881
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Metadata Reflection API
NOTE: Now that both Decorators and
Decorator Metadata have achieved Stage 3 within TC39, the API
proposed below is no longer being considered for standardization. However, this package will continue to support
projects that leverage TypeScript's legacy --experimentalDecorators
option as some projects may not be able to migrate
to use standard decorators.
Installation
npm install reflect-metadata
Usage
ES Modules in NodeJS/Browser, TypeScript/Babel, Bundlers
1// - Modifies global `Reflect` object (or defines one in ES5 runtimes). 2// - Supports ESM and CommonJS. 3// - Contains internal polyfills for `Map`, `Set`, and `WeakMap` for older runtimes. 4import "reflect-metadata"; 5 6// - Modifies global `Reflect` object (or defines one in ES5 runtimes). 7// - Supports ESM and CommonJS. 8// - Requires runtime support for `"exports"` in `package.json`. 9// - Does not include internal polyfills. 10import "reflect-metadata/lite";
CommonJS
1// - Modifies global `Reflect` object (or defines one in ES5 runtimes). 2// - Contains internal polyfills for `Map`, `Set`, and `WeakMap` for older runtimes. 3require("reflect-metadata"); 4 5// - Modifies global `Reflect` object (or defines one in ES5 runtimes). 6// - Requires runtime support for `"exports"` in `package.json`. 7// - Does not include internal polyfills. 8require("reflect-metadata/lite");
In the Browser via <script>
HTML
1<!-- Modifies global `Reflect` object (or defines one in ES5 runtimes). --> 2<!-- Contains internal polyfills for `Map`, `Set`, and `WeakMap` for older runtimes. --> 3<script src="path/to/reflect-metadata/Reflect.js"></script> 4 5<!-- Modifies global `Reflect` object (or defines one in ES5 runtimes). --> 6<!-- Does not include internal polyfills. --> 7<script src="path/to/reflect-metadata/ReflectLite.js"></script>
Script
1// - Makes types available in your editor. 2/// <reference path="path/to/reflect-metadata/standalone.d.ts" /> 3
Background
- Decorators add the ability to augment a class and its members as the class is defined, through a declarative syntax.
- Traceur attaches annotations to a static property on the class.
- Languages like C# (.NET), and Java support attributes or annotations that add metadata to types, along with a reflective API for reading metadata.
Goals
- A number of use cases (Composition/Dependency Injection, Runtime Type Assertions, Reflection/Mirroring, Testing) want the ability to add additional metadata to a class in a consistent manner.
- A consistent approach is needed for various tools and libraries to be able to reason over metadata.
- Metadata-producing decorators (nee. "Annotations") need to be generally composable with mutating decorators.
- Metadata should be available not only on an object but also through a Proxy, with related traps.
- Defining new metadata-producing decorators should not be arduous or over-complex for a developer.
- Metadata should be consistent with other language and runtime features of ECMAScript.
Syntax
- Declarative definition of metadata:
1class C { 2 @Reflect.metadata(metadataKey, metadataValue) 3 method() { 4 } 5}
- Imperative definition of metadata:
1Reflect.defineMetadata(metadataKey, metadataValue, C.prototype, "method");
- Imperative introspection of metadata:
1let obj = new C(); 2let metadataValue = Reflect.getMetadata(metadataKey, obj, "method");
Semantics
- Object has a new [[Metadata]] internal property that will contain a Map whose keys are property keys (or undefined) and whose values are Maps of metadata keys to metadata values.
- Object will have a number of new internal methods for [[DefineOwnMetadata]], [[GetOwnMetadata]], [[HasOwnMetadata]], etc.
- These internal methods can be overridden by a Proxy to support additional traps.
- These internal methods will by default call a set of abstract operations to define and read metadata.
- The Reflect object will expose the MOP operations to allow imperative access to metadata.
- Metadata defined on class declaration C is stored in C.[[Metadata]], with undefined as the key.
- Metadata defined on static members of class declaration C are stored in C.[[Metadata]], with the property key as the key.
- Metadata defined on instance members of class declaration C are stored in C.prototype.[[Metadata]], with the property key as the key.
API
1// define metadata on an object or property
2Reflect.defineMetadata(metadataKey, metadataValue, target);
3Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
4
5// check for presence of a metadata key on the prototype chain of an object or property
6let result = Reflect.hasMetadata(metadataKey, target);
7let result = Reflect.hasMetadata(metadataKey, target, propertyKey);
8
9// check for presence of an own metadata key of an object or property
10let result = Reflect.hasOwnMetadata(metadataKey, target);
11let result = Reflect.hasOwnMetadata(metadataKey, target, propertyKey);
12
13// get metadata value of a metadata key on the prototype chain of an object or property
14let result = Reflect.getMetadata(metadataKey, target);
15let result = Reflect.getMetadata(metadataKey, target, propertyKey);
16
17// get metadata value of an own metadata key of an object or property
18let result = Reflect.getOwnMetadata(metadataKey, target);
19let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey);
20
21// get all metadata keys on the prototype chain of an object or property
22let result = Reflect.getMetadataKeys(target);
23let result = Reflect.getMetadataKeys(target, propertyKey);
24
25// get all own metadata keys of an object or property
26let result = Reflect.getOwnMetadataKeys(target);
27let result = Reflect.getOwnMetadataKeys(target, propertyKey);
28
29// delete metadata from an object or property
30let result = Reflect.deleteMetadata(metadataKey, target);
31let result = Reflect.deleteMetadata(metadataKey, target, propertyKey);
32
33// apply metadata via a decorator to a constructor
34@Reflect.metadata(metadataKey, metadataValue)
35class C {
36 // apply metadata via a decorator to a method (property)
37 @Reflect.metadata(metadataKey, metadataValue)
38 method() {
39 }
40}
Alternatives
- Use properties rather than a separate API.
- Obvious downside is that this can be a lot of code:
1function ParamTypes(...types) { 2 return (target, propertyKey) => { 3 const symParamTypes = Symbol.for("design:paramtypes"); 4 if (propertyKey === undefined) { 5 target[symParamTypes] = types; 6 } 7 else { 8 const symProperties = Symbol.for("design:properties"); 9 let properties, property; 10 if (Object.prototype.hasOwnProperty.call(target, symProperties)) { 11 properties = target[symProperties]; 12 } 13 else { 14 properties = target[symProperties] = {}; 15 } 16 if (Object.prototype.hasOwnProperty.call(properties, propertyKey)) { 17 property = properties[propertyKey]; 18 } 19 else { 20 property = properties[propertyKey] = {}; 21 } 22 property[symParamTypes] = types; 23 } 24 }; 25}
Notes
- Though it may seem counterintuitive, the methods on Reflect place the parameters for the metadata key and metadata value before the target or property key. This is due to the fact that the property key is the only optional parameter in the argument list. This also makes the methods easier to curry with Function#bind. This also helps reduce the overall footprint and complexity of a metadata-producing decorator that could target both a class or a property:
1function ParamTypes(...types) { 2 // as propertyKey is effectively optional, its easier to use here 3 return (target, propertyKey) => { Reflect.defineMetadata("design:paramtypes", types, target, propertyKey); } 4 5 // vs. having multiple overloads with the target and key in the front: 6 // 7 // return (target, propertyKey) => { 8 // if (propertyKey === undefined) { 9 // Reflect.defineMetadata(target, "design:paramtypes", types); 10 // } 11 // else { 12 // Reflect.defineMetadata(target, propertyKey, "design:paramtypes", types); 13 // } 14 // } 15 // 16 // vs. having a different methods for the class or a property: 17 // 18 // return (target, propertyKey) => { 19 // if (propertyKey === undefined) { 20 // Reflect.defineMetadata(target, "design:paramtypes", types); 21 // } 22 // else { 23 // Reflect.definePropertyMetadata(target, propertyKey, "design:paramtypes", types); 24 // } 25 // } 26}
- To enable experimental support for metadata decorators in your TypeScript project, you must add
"experimentalDecorators": true
to your tsconfig.json file. - To enable experimental support for auto-generated type metadata in your TypeScript project, you must add
"emitDecoratorMetadata": true
to your tsconfig.json file.- Please note that auto-generated type metadata may have issues with circular or forward references for types.
Issues
- A poorly written mutating decorator for a class constructor could cause metadata to become lost if the prototype chain is not maintained. Though, not maintaining the prototype chain in a mutating decorator for a class constructor would have other negative side effects as well. @rbuckton
- This is mitigated if the mutating decorator returns a class expression that extends from the target, or returns a proxy for the decorator. @rbuckton
- Metadata for a method is attached to the class (or prototype) via the property key. It would not then be available if trying to read metadata on the function of the method (e.g. "tearing-off" the method from the class). @rbuckton
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0
Reason
Found 3/15 approved changesets -- score normalized to 2
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/ci.yml:1
- Info: no jobLevel write permissions found
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/rbuckton/reflect-metadata/ci.yml/main?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/rbuckton/reflect-metadata/ci.yml/main?enable=pin
- Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:27
- Info: 0 out of 2 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 1 npmCommand dependencies pinned
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 27 are checked with a SAST tool
Score
4
/10
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to reflect-metadata
@inversifyjs/reflect-metadata-utils
Reflect metadata utils
@abraham/reflection
Lightweight ES Module implementation of reflect-metadata
@rxap/reflect-metadata
[![npm version](https://img.shields.io/npm/v/@rxap/reflect-metadata?style=flat-square)](https://www.npmjs.com/package/@rxap/reflect-metadata) [![commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)](http
reflect-metadata-util
reflect-metadata util