Lightweight ES Module implementation of reflect-metadata to work with TypeScript's experimental decorator support, plus a matching API for standard decorators and decorator metadata.
The main reason for this library is to provide a much smaller implementation that can be included as a module.
- ES module
reflectioncan be loaded with<script type="module" src="..."></script>
- Size (uncompressed)
reflect-metadatais ~50Kcore-js/es7/reflectis ~80K@abraham/reflectionis ~3K
Read about how to drop 20K from your production Angular app by switching to this.
npm install @abraham/reflectionUse the one that matches how your project compiles decorators. A project uses one or the other, not both.
@abraham/reflection/legacy (default) |
@abraham/reflection/standard |
|
|---|---|---|
| Decorators | Legacy: experimentalDecorators: true |
Standard: TypeScript 5.2+ without experimentalDecorators, or Babel |
| Type metadata | design:type, design:paramtypes, design:returntype with emitDecoratorMetadata |
Not available |
| Parameter decorators | Yes | No |
| Globals | Adds methods to Reflect |
Defines Symbol.metadata and Function.prototype[Symbol.metadata] (null) if missing, as in the spec |
| Module formats | ES module and UMD/CommonJS | ES module |
Use @abraham/reflection/legacy for code written for reflect-metadata, including frameworks that rely on emitDecoratorMetadata for dependency injection. The bare @abraham/reflection import is the same module, so existing imports keep working and both can be mixed.
Use @abraham/reflection/standard for new code on standard decorators. They don't pass the class to member decorators, so Reflect.defineMetadata can't be used; metadata goes through the decorator's context.metadata instead.
tsconfig.json:
import '@abraham/reflection/legacy'; // or '@abraham/reflection'
Reflect.defineMetadata(metadataKey, metadataValue, target);You can also import Reflection:
import { Reflection as Reflect } from '@abraham/reflection/legacy';
Reflect.defineMetadata(metadataKey, metadataValue, target);Requires TypeScript 5.2 or later. tsconfig.json:
{
"compilerOptions": {
// Leave experimentalDecorators and emitDecoratorMetadata unset or false
// Any target except ESNext, which leaves @decorator syntax untransformed
"target": "ES2022",
// Needed to resolve the ./standard subpath export
"moduleResolution": "bundler", // or "nodenext"
// Only needed to type Symbol.metadata in your own code; add to your existing lib
"lib": ["ES2022", "ESNext.Decorators"],
},
}Import it before any decorated class is defined, so Symbol.metadata exists when the class is created.
import { metadata, getMetadata } from '@abraham/reflection/standard';
@metadata('role', 'admin')
class User {
@metadata('column', 'email')
email = '';
@metadata('route', '/users')
static list() {}
}
getMetadata('role', User); // 'admin'
getMetadata('column', User.prototype, 'email'); // 'email'
getMetadata('column', new User(), 'email'); // 'email'
getMetadata('route', User, 'list'); // '/users'Targets follow reflect-metadata: the class for class and static member metadata, and the prototype or an instance for instance member metadata. Subclasses inherit metadata and can override it. Values are stored privately, so the class's shared Symbol.metadata object is left for other libraries.
The following methods are available:
Reflect.decorate(...);
Reflect.defineMetadata(...);
Reflect.deleteMetadata(...);
Reflect.getMetadata(...);
Reflect.getMetadataKeys(...);
Reflect.getOwnMetadata(...);
Reflect.getOwnMetadataKeys(...);
Reflect.hasMetadata(...);
Reflect.hasOwnMetadata(...);
Reflect.metadata(...);metadata(metadataKey, metadataValue); // decorator for classes, methods, getters, setters, fields, and accessors
getMetadata(metadataKey, target, memberKey?);
getMetadataKeys(target, memberKey?);
getOwnMetadata(metadataKey, target, memberKey?);
getOwnMetadataKeys(target, memberKey?);
hasMetadata(metadataKey, target, memberKey?);
hasOwnMetadata(metadataKey, target, memberKey?);
{ "compilerOptions": { "experimentalDecorators": true, // Only needed for design:type, design:paramtypes, and design:returntype "emitDecoratorMetadata": true, // Needed for the ./legacy subpath; the bare import resolves with any setting "moduleResolution": "bundler", // or "nodenext" }, }