@semaver/reflector
v2.1.1
Published
Reflection framework for TypeScript and JavaScript, with decorator inheritance support and no reflect-metadata dependency.
Readme
@semaver/reflector
A reflection framework for TypeScript and JavaScript that supports decorator inheritance without relying on reflect-metadata.
About
The reflector package offers a comprehensive examination of TypeScript classes. It provides an extensive mechanism for querying and filtering detailed information about class structures. The package supports reflection of decorated class members and parameters through the built-in concept of Annotation Decorators. Annotation Decorators enable the labeling of various class members and parameters in TypeScript. The reflector package handles advanced cases, such as dynamic runtime decoration and inheritance of decorated class members.
Features
- Acquire detailed class information via annotated class members and parameters.
- Support for both static and dynamic annotation (static: via
@decorators; dynamic: at runtime, e.g., for third-party libraries). - Manage inheritance of annotated class members and parameters using Decoration Policies.
Requirements
To use the @decorator() syntax in TypeScript, you must configure the tsconfig.json file. However, it is possible to bypass this requirement by using dynamic decoration, which also works in JavaScript projects.
{
"compilerOptions": {
"experimentalDecorators": true
}
}:bulb: Only
experimentalDecoratorsis required.emitDecoratorMetadatais not needed — this library reads its own__metadata__and does not rely onreflect-metadata/design:typemetadata.
:warning: Important — transpiler support for parameter decorators. This library relies on legacy decorators (
experimentalDecorators) and decorates method/constructor parameters. Your build must use a transpiler that emits legacy parameter decorators. Verified support:
tsc(TypeScript compiler) — full support out of the box (recommended).- SWC — supported via
jsc.transform.legacyDecorator: true.- Babel — requires
@babel/plugin-proposal-decoratorsconfigured for legacy decorators (Babel 8:{ version: 'legacy' }; Babel 7:{ legacy: true }), which transforms parameter decorators. Because this library uses its own metadata (notreflect-metadata), thebabel-plugin-transform-typescript-metadataplugin is optional and not required.- esbuild — legacy decorators only, no parameter-metadata; not recommended for this use case.
TC39 standard (stage-3) decorators are not supported and cannot be: they do not include parameter decorators (that proposal is stalled at Stage 1), so
experimentalDecoratorsremains the required and long-term-supported path.emitDecoratorMetadatais optional for this library (it uses its own metadata, notreflect-metadata).
Installation
$ yarn add @semaver/reflector --peer
$ npm install @semaver/reflector :warning: Important! Please note that this package has a peer dependency on the
@semaver/corelibrary.:warning: Important! Install the library as a peer dependency if possible.
Get Started
To get familiar with the Reflector API, we will explore some simple examples. These examples are modeled after a dependency injection framework (an Injector) to show a realistic use case.
Processing the dependency tree is outside the scope of this example. Instead, we will focus on defining decorators and retrieving information about them using the Reflector.
:information_source: Note: The
@inject,@optional,@named,@postConstruct, and@preDestroydecorators below are not part of@semaver/reflector. They are example decorators, defined in user code on top of the package's Annotation Decorators mechanism (viaDecorator.build), purely to illustrate a dependency-injection scenario. The package ships the reflection engine and theDecoratorbase class — you define your own annotation decorators like these.
Decorators Used in This Example
@inject(type: IType)
In this example, we define a decorator that marks class members for dependency injection, where the type parameter is a class or interface. It can be applied to constructor parameters, instance properties, and instance setters, but not to other class members. If a class inherits properties or setters with this decorator from a superclass, the decorator will be applied to the current class. If the current class has its own decorator of this type, it will override the one from the superclass. For decorators on constructor parameters, we follow the rules based on "Annotation Decorators Usage for Constructor Parameters". The inheritance rules are applied by default.
The type parameter can be a class or an interface.
import { IType } from "@semaver/core";
import { Decorator, DecoratorFn, MetadataAccessPolicy, MetadataAccessPolicyValues } from "@semaver/reflector";
export function inject(type: IType<object>): DecoratorFn {
return Decorator.build(new InjectDecorator(type));
}
export class InjectDecorator extends Decorator {
protected readonly _type: IType<object>;
public constructor(type: IType<object>) {
super();
this._type = type;
}
public getType(): IType<object> {
return this._type;
}
public getAccessPolicy(): MetadataAccessPolicyValues {
return (
MetadataAccessPolicy.INSTANCE_PROPERTY |
MetadataAccessPolicy.INSTANCE_ACCESSOR |
MetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR
);
}
public getParameters(): ReadonlyArray<unknown> {
return [this._type];
}
}@optional()
Marks dependency injection as optional. This decorator can be applied to properties, setters, and constructor parameters, though its use on constructor parameters may be restricted depending on the dependency injection framework. The inheritance rules are the same as those for the @inject decorator.
import { Decorator, DecoratorFn, MetadataAccessPolicy, MetadataAccessPolicyValues } from "@semaver/reflector";
export function optional(): DecoratorFn {
return Decorator.build(new OptionalDecorator());
}
export class OptionalDecorator extends Decorator {
public getAccessPolicy(): MetadataAccessPolicyValues {
return (
MetadataAccessPolicy.INSTANCE_PROPERTY |
MetadataAccessPolicy.INSTANCE_ACCESSOR |
MetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR
);
}
}@named(id: string | number | symbol)
Allows injections by ID. This decorator can be applied to properties, setters, and constructor parameters, following the same inheritance rules as @inject and @optional.
The id parameter can be a string, number, or symbol.
import { Decorator, DecoratorFn, MetadataAccessPolicy, MetadataAccessPolicyValues } from "@semaver/reflector";
type DependencyId = string | number | symbol;
export function named(id: DependencyId): DecoratorFn {
return Decorator.build(new NamedDecorator(id));
}
export class NamedDecorator extends Decorator {
protected readonly _name: DependencyId;
public constructor(name: DependencyId) {
super();
this._name = name;
}
public getName(): DependencyId {
return this._name;
}
public getAccessPolicy(): MetadataAccessPolicyValues {
return (
MetadataAccessPolicy.INSTANCE_PROPERTY |
MetadataAccessPolicy.INSTANCE_ACCESSOR |
MetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR
);
}
public getParameters(): ReadonlyArray<unknown> {
return [this._name];
}
}@postConstruct()
This decorator allows methods to execute after all dependencies have been injected. It can only be applied to instance methods and follows default inheritance rules, so multiple @postConstruct methods are allowed.
import { Decorator, DecoratorFn, MetadataAccessPolicy, MetadataAccessPolicyValues } from "@semaver/reflector";
export function postConstruct(): DecoratorFn {
return Decorator.build(new PostConstructDecorator());
}
export class PostConstructDecorator extends Decorator {
public getAccessPolicy(): MetadataAccessPolicyValues {
return MetadataAccessPolicy.INSTANCE_METHOD;
}
}@preDestroy()
This decorator allows methods to execute before destruction. Like @postConstruct, it can only be applied to instance methods and follows default inheritance rules, allowing multiple @preDestroy methods.
import { Decorator, DecoratorFn, MetadataAccessPolicy, MetadataAccessPolicyValues } from "@semaver/reflector";
export function preDestroy(): DecoratorFn {
return Decorator.build(new PreDestroyDecorator());
}
export class PreDestroyDecorator extends Decorator {
public getAccessPolicy(): MetadataAccessPolicyValues {
return MetadataAccessPolicy.INSTANCE_METHOD;
}
}Using the Decorators
Let's define the classes that use these decorators:
- A base class that inherits from a third-party class and contains
@injectand@nameddecorators applied to a property. - The
@injectdecorator is applied to the first constructor parameter. - The
@injectand@optionaldecorators are applied to the second constructor parameter. - The
@postConstructdecorator is applied to a method.
:warning: The order of decorators can be arbitrary.
import { inject, named, optional, postConstruct } from "./decorators";
import { SomeType } from "./SomeType";
import { AnotherTypeInterface } from "./AnotherTypeInterface";
import { ThirdPartyClass } from "./ThirdPartyClass";
export class BaseClass extends ThirdPartyClass {
@inject(SomeType)
@named("someProperty")
public someProperty: SomeType | undefined;
public constructor(
@inject(SomeType) firstParam: SomeType,
@inject(AnotherTypeInterface) @optional() secondParam?: AnotherTypeInterface
) {
super();
// handle firstParam
// handle secondParam
}
@postConstruct()
public initialize(): void {
// Initialization logic after dependencies are injected
console.log("BaseClass initialized");
}
}A Derived Child Class
This class inherits from the base class and includes an additional @inject for a setter and the @postConstruct decorator.
import { BaseClass } from "./BaseClass";
import { SomeType } from "./SomeType";
import { AnotherType } from "./AnotherType";
import { inject, postConstruct } from "./decorators";
export class ChildClass extends BaseClass {
private _someOtherProperty: SomeType | undefined;
@inject(SomeType)
public set someOtherProperty(value: SomeType | undefined) {
this._someOtherProperty = value;
}
public constructor() {
super(new SomeType(), new AnotherType());
}
@postConstruct()
public initializeChild(): void {
// Additional initialization logic specific to ChildClass
console.log("ChildClass initialized");
}
}Another Derived Child Class
This class inherits from the base class but does not include any additional decorators.
import { BaseClass } from "./BaseClass";
export class AnotherChildClass extends BaseClass {
// No additional decorators
public someAdditionalMethod(): void {
// method body
}
}Implementation in the Injector
In the implementation of the Injector, we need to retrieve information about decorated classes:
Define an interface to store the retrieved data about decorations for later use:
import { Method, Parameter, Field } from "@semaver/reflector"; import { IClass, IType, Nullable } from "@semaver/core"; type DependencyId = string | number | symbol; interface InjectionInfo<T extends IDecoratedElement> { type: IType<object>; named?: DependencyId; optional?: boolean; classMember: T; } interface MetaInfo { decoratedClass: IClass<object>; constructorParameters: InjectionInfo<Parameter>[]; fields: Record<string, InjectionInfo<Field>>; postConstructs: Method[]; preDestroys: Method[]; }Identify all classes that have the
@injectdecorator.Retrieve all decorated classes from the class table:
const classTable: IClassTable = Reflector.getClassTable(); const classes: ReadonlySet<IClass<object>> = classTable.getClasses();For this example, the set contains only two classes:
BaseClassandChildClass, but notAnotherChildClass. SinceAnotherChildClassdoes not have its own decorators, it is not in the class table. To addAnotherChildClassto the class table, we can use the built-in decorator@metaclass, which can only be applied at the class (constructor) level and is not inherited by child classes:import { metaclass } from "@semaver/reflector"; import { BaseClass } from "./BaseClass"; @metaclass() export class AnotherChildClass extends BaseClass { // No additional decorators public someAdditionalMethod(): void { // method body } }Alternatively, this can be done dynamically:
Reflector.from(AnotherChildClass).getConstructor().addDecorator(metaclass());Now we can iterate through all three decorated classes, trying to find classes that contain
@injectdecorators:const classTable: IClassTable = Reflector.getClassTable(); const classes: ReadonlySet<IClass<object>> = classTable.getClasses(); const metaInfoByClass: Map<IClass<object>, MetaInfo> = new Map<IClass<object>, MetaInfo>(); classes.forEach((decoratedClass: IClass<object>) => { const reflector: Reflector = Reflector.from(decoratedClass); if ( reflector.query().filter(ByDecoratorClass.from(InjectDecorator)) .members().all().length > 0 ) { metaInfoByClass.set(decoratedClass, proceedDecoratedClass(decoratedClass)); } });
Implement the
proceedDecoratedClassfunction to retrieve detailed information about classes with@injectdecorators. It consists of three parts:- Retrieve information about constructor parameters.
- Retrieve information about fields (setters and properties).
- Retrieve information about methods for
postConstructandpreDestroy.
const proceedDecoratedClass = (decoratedClass: IClass<object>): MetaInfo => { const metaInfo: MetaInfo = { decoratedClass: decoratedClass, constructorParameters: [], fields: {}, postConstructs: [], preDestroys: [], }; const reflector: Reflector = Reflector.from(decoratedClass); // PART 1: Constructor Parameters reflector.getConstructor().getParameters().forEach((parameter) => { if (parameter.hasDecorators(InjectDecorator)) { const inject = parameter.getDecorators(InjectDecorator)[0] as InjectDecorator; const optional = parameter.getDecorators(OptionalDecorator)[0] as OptionalDecorator; const named = parameter.getDecorators(NamedDecorator)[0] as NamedDecorator; metaInfo.constructorParameters.push({ type: inject.getType(), named: named?.getName(), optional: !!optional, classMember: parameter, }); } }); // PART 2: Fields (Setters and Properties) reflector.getDecoratedFields().forEach((field) => { if (field.hasDecorators(InjectDecorator)) { const inject = field.getDecorators(InjectDecorator)[0] as InjectDecorator; const optional = field.getDecorators(OptionalDecorator)[0] as OptionalDecorator; const named = field.getDecorators(NamedDecorator)[0] as NamedDecorator; metaInfo.fields[field.getName()] = { type: inject.getType(), named: named?.getName(), optional: !!optional, classMember: field, }; } }); // PART 3: Methods (PostConstruct and PreDestroy) reflector.getDecoratedMethods().forEach((method) => { if (method.hasDecorators(PostConstructDecorator)) { metaInfo.postConstructs.push(method); } else if (method.hasDecorators(PreDestroyDecorator)) { metaInfo.preDestroys.push(method); } }); return metaInfo; };Now the information about decorations can be processed by the logic of the injector!
If there's a need to decorate third-party classes without access to the source code, it can be done dynamically:
import { SomeType } from "./SomeType"; import { inject, optional } from "./decorators"; export class ThirdPartyClass { public get thirdPartyProperty(): SomeType | undefined { return this._thirdPartyProperty; } public set thirdPartyProperty(value: SomeType | undefined) { this._thirdPartyProperty = value; } private _thirdPartyProperty: SomeType | undefined; } Reflector.from(ThirdPartyClass) .getField("thirdPartyProperty") .addDecorator(optional()) .addDecorator(inject(SomeType));If you need to use an interface as a parameter for the
@injectdecorator, it can be done by extending the interface using an interface symbol:import { IInterface, InterfaceSymbol } from "@semaver/core"; export const AnotherTypeInterface: IInterface<AnotherTypeInterface> = InterfaceSymbol.for("AnotherTypeInterface"); export interface AnotherTypeInterface { someMethod(): void; }Now it is possible to use it like this:
@inject(AnotherTypeInterface).
Reflector Documentation
- Reflector API
- Annotation Decorators
- Architectural Notes
Reflector API
The reflector package provides a powerful API for accessing and querying class structures in TypeScript. It allows you to retrieve information about class members and parameters, especially those annotated with Annotation Decorators.
Note: Annotation Decorators are a mechanism provided by this package for labeling different class members and parameters. To utilize the Reflector effectively, ensure your annotations are built using this mechanism. Learn more about creating and using annotation decorators in the Annotation Decorators section.
Creating a Reflector Instance
You can create a Reflector instance in two ways:
- Using a Class Type
- Using a Class Instance
Example Usage
import { Reflector } from "@semaver/reflector";
import { SomeClass } from "./SomeClass";
// Creating Reflector from a class type
const reflectorFromType: Reflector = Reflector.from(SomeClass);
// Creating Reflector from a class instance
const instance = new SomeClass();
const reflectorFromInstance: Reflector = Reflector.from(instance);Reflector.from() Method
The Reflector.from() method accepts two parameters:
target: The class type or instance you want to reflect.autoSync(optional): A boolean value indicating whether the reflector should automatically synchronize with dynamic decorations. Defaults tofalse.
Parameter Details:
target: This can be either the class itself or an instance of the class whose structure you wish to examine.autoSync:- When set to
true, the reflector will automatically detect and include any changes made through dynamic decoration. - When set to
false(default), the reflector will not track dynamic changes.
- When set to
Example with autoSync:
// Reflector with autoSync enabled
const autoSyncReflector: Reflector = Reflector.from(SomeClass, true);Note: For more information on dynamic decoration, refer to the Dynamic Decoration section.
Reflected Types
The Reflector API can access four types of class members and one parameter type:
//---PRIMITIVE DECORATED ELEMENTS---------------------------------------------------------------------------
interface PrimitiveDecoratedElementType {
CONSTRUCTOR: number
PROPERTY: number;
ACCESSOR: number;
METHOD: number;
CONSTRUCTOR_PARAMETER: number;
METHODS_PARAMETER: number;
}
const PrimitiveDecoratedElementEnum: Readonly<PrimitiveDecoratedElementType> = Object.freeze({
CONSTRUCTOR: 1,
PROPERTY: 2,
ACCESSOR: 4,
METHOD: 8,
CONSTRUCTOR_PARAMETER: 16,
METHODS_PARAMETER: 32,
});
//---ADVANCED DECORATED ELEMENTS---------------------------------------------------------------------------
export interface DecoratedElementType extends PrimitiveDecoratedElementType {
CLASS_MEMBER: number;
DECORATED_ELEMENT: number;
EXECUTABLE_ELEMENT: number;
FIELD: number;
PARAMETER: number;
}
export const DecoratedElementEnum: Readonly<DecoratedElementType> = Object.freeze({
CONSTRUCTOR: PrimitiveDecoratedElementEnum.CONSTRUCTOR,
METHOD: PrimitiveDecoratedElementEnum.METHOD,
EXECUTABLE_ELEMENT: PrimitiveDecoratedElementEnum.METHOD |
PrimitiveDecoratedElementEnum.CONSTRUCTOR,
PROPERTY: PrimitiveDecoratedElementEnum.PROPERTY,
ACCESSOR: PrimitiveDecoratedElementEnum.ACCESSOR,
FIELD: PrimitiveDecoratedElementEnum.PROPERTY |
PrimitiveDecoratedElementEnum.ACCESSOR,
CLASS_MEMBER: PrimitiveDecoratedElementEnum.PROPERTY |
PrimitiveDecoratedElementEnum.ACCESSOR |
PrimitiveDecoratedElementEnum.METHOD |
PrimitiveDecoratedElementEnum.CONSTRUCTOR,
CONSTRUCTOR_PARAMETER: PrimitiveDecoratedElementEnum.CONSTRUCTOR_PARAMETER,
METHODS_PARAMETER: PrimitiveDecoratedElementEnum.METHODS_PARAMETER,
PARAMETER: PrimitiveDecoratedElementEnum.CONSTRUCTOR_PARAMETER |
PrimitiveDecoratedElementEnum.METHODS_PARAMETER,
DECORATED_ELEMENT: PrimitiveDecoratedElementEnum.PROPERTY |
PrimitiveDecoratedElementEnum.ACCESSOR |
PrimitiveDecoratedElementEnum.METHOD |
PrimitiveDecoratedElementEnum.CONSTRUCTOR |
PrimitiveDecoratedElementEnum.CONSTRUCTOR_PARAMETER |
PrimitiveDecoratedElementEnum.METHODS_PARAMETER,
});
export type DecoratedElementTypeValues = DecoratedElementType[keyof DecoratedElementType];These reflected types are wrapper classes around the corresponding language constructs, providing encapsulated information about the requested class members or parameters.
Note: For more details on reflected types, refer to the Reflected Types Architecture.
Get Class Members and Parameters
To retrieve information from a class, create a Reflector instance and use the relevant API methods. If a class member or parameter is decorated, the Reflector will return additional information about the applied decorators.
Important: When retrieving class members or parameters by name, be aware of the obfuscation/minification level of your code. These processes can alter names, making it impossible for the Reflector to find the requested code.
Example Usage
import { Reflector } from "@semaver/reflector";
import { SomeClass } from "./SomeClass";
const reflector: Reflector = Reflector.from(SomeClass);
//---CONSTRUCTOR---------------------------------------------------------------------------
// Returns the constructor class member (decorated or not). NEVER undefined:
// if neither the constructor nor its parameters carry any decorator, a synthesized
// non-decorated constructor is returned, with parameters derived from the class's
// known constructor parameter count. Contrast with getDecoratedConstructor(),
// which returns undefined when nothing is decorated.
const baseConstructor: Constructor<SomeClass> = reflector.getConstructor();
//---METHOD---------------------------------------------------------------------------------
// BEWARE OF OBFUSCATION/MINIFICATION LEVEL
// Returns instance/static method by name (decorated or not) or else undefined
const methodName = 'myMethod';
const isMethodStatic = false;
const someMethod: Method<SomeClass> = reflector.getMethod(methodName, isMethodStatic);
//---FIELD (UNION OF ACCESSOR & PROPERTY)----------------------------------------------------
// BEWARE OF OBFUSCATION/MINIFICATION LEVEL
// Returns instance/static field (accessor or property) by name (decorated or not).
// NEVER returns undefined: if no decorated field and no matching accessor exist, a
// synthesized Property with the requested name/isStatic is returned even if no such
// member exists on the class. Do not use an undefined-check to test for existence.
const fieldName = 'myField';
const isFieldStatic = false;
const someField: Field<SomeClass> = reflector.getField(fieldName, isFieldStatic);
//---ACCESSOR--------------------------------------------------------------------------------
// BEWARE OF OBFUSCATION/MINIFICATION LEVEL
// Returns instance/static accessor by name (decorated or not) or else undefined
const accessorName = 'myAccessor';
const isAccessorStatic = false;
const someAccessor: Accessor<SomeClass> = reflector.getAccessor(accessorName, isAccessorStatic);
//---PROPERTY--------------------------------------------------------------------------------
// BEWARE OF OBFUSCATION/MINIFICATION LEVEL
// Returns instance/static property by name (decorated or not).
// NEVER returns undefined: on a miss a synthesized Property with the requested
// name/isStatic is returned regardless of whether the property actually exists on
// the class (its runtime value may be undefined). Not suitable for existence checks.
const propertyName = 'myProperty';
const isPropertyStatic = false;
const someProperty: Property<SomeClass> = reflector.getProperty(propertyName, isPropertyStatic);Additional Methods for Annotation Decorators
To facilitate working with class annotation decorators, the Reflector API provides additional decorator-specific methods:
const reflector: Reflector = Reflector.from(SomeClass);
//---ADDITIONAL ANNOTATION DECORATOR-SPECIFIC METHODS------------------------------------------------------
// Returns the constructor if it or any of its parameters are decorated, else undefined
const decoratedConstructor: Constructor<SomeClass> = reflector.getDecoratedConstructor();
// Returns all decorated methods or an empty array if none are found
const methods: ReadonlyArray<Method<SomeClass>> = reflector.getDecoratedMethods();
// Returns all decorated fields (accessors and properties) or an empty array if none are found
const fields: ReadonlyArray<Field<SomeClass>> = reflector.getDecoratedFields();
// Returns all decorated accessors or an empty array if none are found
const accessors: ReadonlyArray<Accessor<SomeClass>> = reflector.getDecoratedAccessors();
// Returns all decorated properties or an empty array if none are found
const properties: ReadonlyArray<Property<SomeClass>> = reflector.getDecoratedProperties();
// Returns all decorated constructors, methods, accessors, fields, or an empty array if none are found
const members: ReadonlyArray<ClassMember<SomeClass>> = reflector.getDecoratedMembers();
// Refreshes the class info, then returns the current synchronization hash of the target class.
// Use it to detect metadata/decorator changes and invalidate your own caches keyed on this value.
// This is the per-class hash from the Reflector instance, distinct from ClassTable.getSyncHash().
const hash: string = reflector.getHash();Query Class Members and Parameters
The Query API allows you to retrieve different class members and parameters. However, the reflector queries work only with elements that are annotated using Annotation Decorators. Non-annotated elements will not be visible to the query mechanism.
Note: For details on creating and using annotation decorators, see the Annotation Decorators section.
Example Usage
import { ClassMember, Reflector, QueryExecutor, QueryMembersSelector } from "@semaver/reflector";
import { MyClass } from "./MyClass";
// Returns a query executor for the class
const query: QueryExecutor<MyClass> = Reflector.from(MyClass).query();
//---CLASS MEMBERS---------------------------------------------------------------------------
// Returns a member selector from the query
const memberSelector: QueryMembersSelector<MyClass> = query.members();
// Returns all class members from the member selector
const members: ClassMember[] = memberSelector.all<ClassMember>();
// Returns the first class member from the filtered collection, or `undefined` if no members matched (callers must null-check)
const firstFoundMember: ClassMember | undefined = memberSelector.first<ClassMember>();Additional Query API for Annotation Decorators
The Reflector provides an additional Query API specifically for annotation decorators. Since Annotation Decorators support inheritance, it’s essential to distinguish between decorators that will actually be executed and those that are explicitly assigned to a class member or parameter. The actual execution may be influenced by decorator inheritance and Decoration Policies, if defined.
Note: For more information on decoration policies, refer to the Decoration Policies section.
//---CLASS MEMBER & PARAMETER ANNOTATION DECORATORS----------------------------------------------------
// Returns all decorators from the query, including inherited decorators and those affected by decoration policies
const decoratorSelector: QueryDecoratorSelector<MyClass> = query.decorators();
// Returns only the decorators explicitly assigned to the class, excluding inheritance and decoration policies
const ownDecorators: QueryDecoratorSelector<MyClass> = query.ownDecorators();
// Retrieves all decorators: a flat union of every selected member's member-level decorators PLUS the parameter decorators of executable members (constructors/methods). Equivalent to ofMembers() + ofParameters() combined.
const decorators: Decorator[] = decoratorSelector.all();
// Retrieves only member-level decorators from every selected class member
const memberDecorators: Decorator[] = decoratorSelector.ofMembers();
// Retrieves only parameter decorators, gathered exclusively from executable members (constructors/methods); fields and accessors have no parameters and contribute none
const parameterDecorators: Decorator[] = decoratorSelector.ofParameters();
// Note: whether these three methods return own-only decorators or the full inherited-and-policy-processed set is determined by the selector they came from — query.ownDecorators() yields own-only, query.decorators() yields the full set — not by the method called.Filter Class Members and Parameters
You can filter class members and parameters using various filtering conditions:
ByMemberName: Filter class members by name.ByMemberType: Filter class members by type (CONSTRUCTOR, METHOD, PROPERTY, ACCESSOR). Types are bit flags and matching is bitwise: a member is kept when its type shares any bit with a supplied type. You may pass several types in one call (e.g.ByMemberType.from(DecoratedElementEnum.METHOD, DecoratedElementEnum.ACCESSOR)), which keeps members of any of those types (logical OR).ByStaticMember: Filter class members by static or non-static.ByDecoratorClass: keep members that carry a decorator of the given class either on the member itself or on any of its parameters (parameters are only inspected for executable members — constructor/methods).ByMemberDecoratorClass: keep members whose member-level decorators include the given class (parameter decorators are ignored).ByParameterDecoratorClass: keep executable members (constructor/methods) with at least one parameter decorated by the given class; non-executable members (fields/accessors) never match.
Note: For
ByDecoratorClass,ByMemberDecoratorClass, andByParameterDecoratorClass, matching is by exact decorator-class identity (constructor equality), notinstanceof— subclasses of the given decorator class do not match. Multiple decorator classes may be passed and are matched as OR (a member matches if it carries any of them).
Filtering is based on the query mechanism described in the Query Class Members and Parameters section. To access filtered values, refer to the Query API.
Important: When retrieving a class member or parameter by name, be cautious of obfuscation/minification levels, as these processes can change names and make reflection impossible.
Important —
filter()narrows the executor in place.filter(condition)narrows the executor's current set of class members in place by applying the given condition, then returns the same executor instance for chaining — it does not clone. Because the underlying query state is shared, afterquery.filter(...)the originalqueryreference is itself narrowed, and chaining multiplefiltercalls ANDs the conditions cumulatively. In the examples below the results are stored in separate variables (byMemberNameQuery,byMemberTypeQuery, etc.) only for readability; they all refer to the same, progressively narrowedqueryobject. To obtain a fresh, unfiltered executor, callReflector.from(MyClass).query()again.
Example Usage
import {
Reflector,
QueryExecutor,
ByMemberName,
ByMemberType,
ByStaticMember,
ByDecoratorClass,
ByParameterDecoratorClass,
ByMemberDecoratorClass,
DecoratedElementEnum,
DecoratedElementTypeValues
} from "@semaver/reflector";
import { MyClass, AnnotationDecorator } from "./MyClass";
const query: QueryExecutor<MyClass> = Reflector.from(MyClass).query();
//---FILTER BY NAME-------------------------------------------------------------------------
// BEWARE OF OBFUSCATION/MINIFICATION LEVEL
const memberName: string = "someClassMemberName";
const byMemberNameQuery: QueryExecutor<MyClass> = query.filter(ByMemberName.from(memberName));
//---FILTER BY MEMBER TYPE------------------------------------------------------------------
const memberType: DecoratedElementTypeValues = DecoratedElementEnum.METHOD;
const byMemberTypeQuery: QueryExecutor<MyClass> = query.filter(ByMemberType.from(memberType));
//---FILTER BY STATIC/NON-STATIC MEMBER------------------------------------------------------
const isMemberStatic: boolean = false;
const byMemberStaticQuery: QueryExecutor<MyClass> = query.filter(ByStaticMember.from(isMemberStatic));
//---FILTER BY ANNOTATION DECORATOR (3 Examples)
const decoratorClass: IClass<AnnotationDecorator> = AnnotationDecorator;
// 1. Filter by annotation decorator for class members & parameters
const byDecoratorClassQuery: QueryExecutor<MyClass> = query.filter(ByDecoratorClass.from(decoratorClass));
// 2. Filter by annotation decorator for parameters only
const byParameterDecoratorClassQuery: QueryExecutor<MyClass> = query.filter(ByParameterDecoratorClass.from(decoratorClass));
// 3. Filter by annotation decorator for class members only
const byMemberDecoratorClassQuery: QueryExecutor<MyClass> = query.filter(ByMemberDecoratorClass.from(decoratorClass));Important —
from()returns a shared cached instance. For every filter condition above, thefrom(...)static factory does not allocate a new object; each condition class keeps a single static cached instance andfrom(...)reconfigures and returns that same instance. This keeps allocation cheap when a condition is applied immediately (as in the example above), but it means two conditions of the same type obtained viafrom()are the same object: a laterfrom()call — or a latersetX()call — overwrites the configuration of an earlier one. If you need to hold a condition and reuse or reconfigure it independently (for example, keeping twoByMemberNameconditions with different names alive at once), construct it directly withnew ByMemberName(...)(all conditions expose a public constructor taking the same arguments asfrom()) instead offrom().
Custom Filters
You can combine or chain different filtering conditions or create custom filters by implementing the IQueryCondition interface. Examples can be found in the existing filters:
Decorate Class Members and Parameters Dynamically
The reflector package, along with its Annotation Decorators, allows you to dynamically add or remove decorators at runtime. Based on the Reflected Types Architecture, each Reflected Type is a child of the DecoratedElement<T> class and inherits the following two methods:
addDecorator(decoratorOrFn: Decorator | DecoratorFn): thisAdds decorators at runtime (returns the same reflected element, so calls can be chained).
decoratorOrFn: The decorator function to be added.
removeDecorator(decoratorOrClass: IClass<Decorator> | Decorator): thisRemoves decorators at runtime.
decoratorOrClass: The decorator class to be removed.
Example Annotation Decorator
Assume we have defined an annotation decorator as follows:
Note: For detailed information on how to create annotation decorators, refer to the Annotation Decorators section.
import { Decorator } from "@semaver/reflector";
export function annotation(): DecoratorFn {
return Decorator.build(new AnnotationDecorator());
}
export class AnnotationDecorator extends Decorator {
// Implementation details
}Refreshing the Reflector
When decorators are added or removed dynamically, the Reflector must be refreshed to recognize these changes. There are two ways to do this:
Enable
autoSyncSet theautoSyncparameter totruewhen creating the Reflector. This will automatically update the Reflector each time the Reflector API is called, keeping it always up-to-date.- By default,
autoSyncisfalse, meaning the Reflector does not update automatically.
const reflector: Reflector<MyClass> = Reflector.from(MyClass, true); // autoSync is true- By default,
Manually Refresh the Reflector If
autoSyncis set tofalse(the default), the Reflector won’t automatically see added or removed decorators. In this case, you need to explicitly update the Reflector using therefresh()method.const reflector: Reflector<MyClass> = Reflector.from(MyClass); // autoSync is false by default reflector.getConstructor().addDecorator(annotation()); // added decorator is not yet visible to the Reflector reflector.refresh(); // explicit update makes the newly added decorator visible to the Reflector
Note:
getHash()andrefresh()always recompute regardless ofautoSync(getHash()callsrefresh()internally). All other accessors —getConstructor,getMethod,getField,getAccessor,getProperty, thegetDecorated*methods, andquery()— only auto-refresh whenautoSyncistrue; otherwise callrefresh()manually after dynamic decoration.refresh()is a cheap no-op when neither the class hash nor the metatable has changed, so it is safe to call frequently.
Complete Reflector API for Dynamic Decoration
Below is the complete API for adding and removing decorators dynamically using the Reflector:
const reflector: Reflector<MyClass> = Reflector.from(MyClass);
//---CONSTRUCTOR---------------------------------------------------------------------------
reflector.getConstructor().addDecorator(annotation());
// OR
reflector.getConstructor().addDecorator(Decorator.build(new AnnotationDecorator()));
reflector.getConstructor().removeDecorator(AnnotationDecorator);
//---CONSTRUCTOR PARAMETERS-----------------------------------------------------------------
reflector.getConstructor().getParameterAt(0)?.addDecorator(annotation());
reflector.getConstructor().getParameterAt(0)?.removeDecorator(AnnotationDecorator);
//---FIELD (UNION OF ACCESSOR & PROPERTY)---------------------------------------------------
// `true` - for instance class member, `false` - for static class members
reflector.getField(memberName, true).addDecorator(annotation());
reflector.getField(memberName, true).removeDecorator(AnnotationDecorator);
//---ACCESSOR-------------------------------------------------------------------------------
// `true` - for instance class member, `false` - for static class members
reflector.getAccessor(memberName, true).addDecorator(annotation());
reflector.getAccessor(memberName, true).removeDecorator(AnnotationDecorator);
//---PROPERTY-------------------------------------------------------------------------------
// `true` - for instance class member, `false` - for static class members
reflector.getProperty(memberName, true).addDecorator(annotation());
reflector.getProperty(memberName, true).removeDecorator(AnnotationDecorator);
//---METHOD---------------------------------------------------------------------------------
reflector.getMethod(memberName, true).addDecorator(annotation());
reflector.getMethod(memberName, true).removeDecorator(AnnotationDecorator);
//---METHOD PARAMETERS-----------------------------------------------------------------------
// Adds or removes decorators for method parameters
reflector.getMethod(memberName, true).getParameterAt(0)?.addDecorator(annotation());
reflector.getMethod(memberName, true).getParameterAt(0)?.removeDecorator(AnnotationDecorator);
reflector.refresh(); // Ensures all dynamic changes are recognized by the ReflectorGet All Decorated Classes
The reflector package stores information about decorated classes (those using Annotation Decorators) in a centralized storage called ClassTable. This storage allows you to retrieve classes that have been explicitly decorated.
Warning: The ClassTable only contains classes that have their own decorators, not those that acquire decorators through inheritance or Decoration Policies. However, you can add such a class to the ClassTable by applying the
@reflect()annotation decorator to it.
Retrieving Decorated Classes from ClassTable
You can retrieve decorated classes from the ClassTable using the following code:
const classTable: IClassTable = Reflector.getClassTable();ClassTable API
The IClassTable interface provides the following methods:
export interface IClassTable {
getClasses(): ReadonlySet<IClass<object>>;
getSyncHash(): string;
subscribe(...subscribers: IClassTableSubscriber[]): this;
unsubscribe(...subscribers: IClassTableSubscriber[]): this;
}getClasses: Returns a set of all decorated classes.getSyncHash: Returns the class table's synchronization hash, an opaque string regenerated by the reflection engine whenever a class is added to, updated with its own metadata, or removed from the table. Cache the value and compare it against a later call: any difference means the set of decorated classes changed, so you can invalidate caches keyed on it. Do not parse or order the string. This is the table-wide hash, distinct from the per-classReflector.getHash().subscribe/unsubscribe: Allows you to subscribe or unsubscribe from ClassTable modifications. Subscribers receive detailed information about what was modified.
ClassTable Subscription API
When subscribing to ClassTable updates, the IClassTableSubscriber interface provides a method to handle updates:
export interface IClassTableSubscriber {
onClassTableUpdate(update: IClassTableUpdate): void;
}The IClassTableUpdate interface provides detailed information about the updates:
export interface IClassTableUpdate<TDecorator extends Decorator = Decorator, T = unknown> {
readonly type: ClassTableUpdateTypes; // Enum: { METADATA_ADDED, METADATA_REMOVED }
readonly decorator: TDecorator;
readonly targetClass: IClass<T>;
readonly decoratedElement: {
readonly type: DecoratedElementTypeValues; // Class member type (Constructor, Method, Parameters, etc.); the runtime enum constant is `DecoratedElementEnum`
readonly name: string; // Name of the class member; for a parameter, the enclosing method's name; for a constructor or constructor parameter, the literal "ctor"
readonly isStatic: boolean; // Indicates if the class member is static
readonly parameterIndex: number; // Index of the parameter, if applicable
};
}type: Indicates the type of update, whether metadata/decorator was added or removed.decorator: The decorator object itself.targetClass: The class that had its decorators modified.decoratedElement: Information about the decorated element:type: The type of class member (e.g., Constructor, Method, Parameters).name: For an ordinary class member (method, accessor, property), its own name. For a parameter, the name of the enclosing method — useparameterIndexto locate the specific parameter. For a constructor or a constructor parameter, the literal string"ctor"(Constructor.defaultName), not the class name.isStatic: Indicates whether the class member is static.parameterIndex: The parameter index if the decorated element is a parameter.
Annotation Decorators
Annotation Decorators is a decoration mechanism provided by the reflector package for TypeScript code annotations. It enables labeling various class members and parameters, which the reflector package then uses to query class structures. Below, you’ll find information on how to create and use annotation decorators to make them available for the Reflector.
For additional insights into how the Reflector retrieves information about annotation decorators, refer to the Architectural Notes: Behind the Scene of Annotation Decorators.
Create Annotation Decorators
You can define an annotation decorator in four steps:
- Define the Decorator Function
- Define the Decorator Class
- (Optional) Define Decoration Policies
- (Optional) Define Decorator Parameters
STEP 1: Define the Decorator Function
Similar to TypeScript decorators, you need to define a decorator function. However, the decorator function must include a call to Decorator.build(), which expects an annotation decorator class of type Decorator (defined in Step 2).
export function annotation(): DecoratorFn {
return Decorator.build(new AnnotationDecorator());
}STEP 2: Define the Decorator Class
Next, define a new AnnotationDecorator class by extending the abstract Decorator class from the reflector package. The main purpose of the AnnotationDecorator class is to define or override decoration policies (Step 3) and specify the accepted parameters for the decorator function (Step 4).
import { Decorator } from "@semaver/reflector";
export function annotation(): DecoratorFn {
return Decorator.build(new AnnotationDecorator());
}
export class AnnotationDecorator extends Decorator {
// Optional: Define decoration policies in Step 3
// Optional: Define decorator parameters in Step 4
}[OPTIONAL] STEP 3: Define Decoration Policies
Decoration policies define rules for how decorators behave in specific cases, particularly related to class inheritance. There are currently five policies:
- Access Policy: Defines whether the decorator can be applied to a specific member, argument, or member group.
- Default:
ALL(applies to all Reflected Types).
- Default:
- Same-Target-Multi-Usage Policy: Determines the behavior when a class member or argument has more than one decorator of the same type.
- Default:
NOT_ALLOWED(only the first decorator is used).
- Default:
- Collision Policy: Handles situations where a decorator exists in both a child class and its parent class for the same member or argument.
- Default:
OVERRIDE_PARENT(the child class decorator is used).
- Default:
- Not-Existence Policy: Governs behavior when a decorator exists in a parent class but not in the child class.
- Default:
APPLY(the parent class decorator is used).
- Default:
- Appearance Policy: Manages behavior when a decorator exists in the child class but not in the parent class.
- Default:
APPLY(the child class decorator is used).
- Default:
Note: For more details on decoration policies and their parameters, see the Decoration Policies section.
Example Implementation:
import {
Decorator,
DecoratorFn,
MetadataAccessPolicy,
MetadataAccessPolicyValues,
MetadataCollisionPolicy,
MetadataCollisionPolicyValues,
MetadataNotExistencePolicy,
MetadataNotExistencePolicyValues,
MetadataSameTargetMultiUsagePolicy,
MetadataSameTargetMultiUsagePolicyValues
} from "@semaver/reflector";
export function annotationWithoutPolicyProvider(): DecoratorFn {
return Decorator.build(new AnnotationWithoutPolicyProviderDecorator());
}
export class AnnotationWithoutPolicyProviderDecorator extends Decorator {
// Allow only instance properties, accessors, and constructor parameters
public getAccessPolicy(): MetadataAccessPolicyValues {
return MetadataAccessPolicy.INSTANCE_PROPERTY |
MetadataAccessPolicy.INSTANCE_ACCESSOR |
MetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR;
}
// Override parent class decorator if both exist on the same member
public getCollisionPolicy(): MetadataCollisionPolicyValues {
return MetadataCollisionPolicy.OVERRIDE_PARENT;
}
// Apply parent class decorator if none exists in the child class
public getNotExistencePolicy(): MetadataNotExistencePolicyValues {
return MetadataNotExistencePolicy.APPLY;
}
// Do not allow multiple decorators on the same class member or parameter
public getSameTargetMultiUsagePolicy(): MetadataSameTargetMultiUsagePolicyValues {
return MetadataSameTargetMultiUsagePolicy.NOT_ALLOWED;
}
}
Policy Provider:
For more granular control, you can define different sets of policies for different reflection types using a Policy Provider.
Example Implementation:
import {
Decorator,
DecoratorFn,
IPolicyProvider,
MetadataAccessPolicy,
MetadataAccessPolicyValues,
MetadataAppearancePolicy,
MetadataAppearancePolicyValues,
MetadataCollisionPolicy,
MetadataCollisionPolicyValues,
MetadataNotExistencePolicy,
MetadataNotExistencePolicyValues,
MetadataSameTargetMultiUsagePolicy,
MetadataSameTargetMultiUsagePolicyValues,
PolicyProvider,
PrimitiveMetadataAccessPolicyValues
} from "@semaver/reflector";
export function annotationWithPolicyProvider(): DecoratorFn {
return Decorator.build(new AnnotationWithPolicyProviderDecorator());
}
export class AnnotationWithPolicyProviderDecorator extends Decorator {
private static policyProvider: IPolicyProvider =
// Allow for methods and properties only
new PolicyProvider(MetadataAccessPolicy.METHOD | MetadataAccessPolicy.PROPERTY)
.setAppearancePolicy(MetadataAppearancePolicy.DEFAULT)
.setCollisionPolicy(MetadataCollisionPolicy.DEFAULT)
.setNotExistencePolicy(MetadataNotExistencePolicy.DEFAULT)
// Allow multiple decorators for instance methods and properties
.setSameTargetMultiUsagePolicy(
MetadataSameTargetMultiUsagePolicy.ALLOWED,
MetadataAccessPolicy.INSTANCE_METHOD | MetadataAccessPolicy.INSTANCE_PROPERTY)
// Do not allow multiple decorators for static methods and properties
.setSameTargetMultiUsagePolicy(
MetadataSameTargetMultiUsagePolicy.NOT_ALLOWED,
MetadataAccessPolicy.STATIC_METHOD | MetadataAccessPolicy.STATIC_PROPERTY);
public getAccessPolicy(): MetadataAccessPolicyValues {
return AnnotationWithPolicyProviderDecorator.policyProvider.getAccessPolicy();
}
public getAppearancePolicy(
access: PrimitiveMetadataAccessPolicyValues): MetadataAppearancePolicyValues {
return AnnotationWithPolicyProviderDecorator.policyProvider.getAppearancePolicy(access);
}
public getNotExistencePolicy(
access: PrimitiveMetadataAccessPolicyValues): MetadataNotExistencePolicyValues {
return AnnotationWithPolicyProviderDecorator.policyProvider.getNotExistencePolicy(access);
}
public getCollisionPolicy(
access: PrimitiveMetadataAccessPolicyValues): MetadataCollisionPolicyValues {
return AnnotationWithPolicyProviderDecorator.policyProvider.getCollisionPolicy(access);
}
public getSameTargetMultiUsagePolicy(
access: PrimitiveMetadataAccessPolicyValues): MetadataSameTargetMultiUsagePolicyValues {
return AnnotationWithPolicyProviderDecorator.policyProvider.getSameTargetMultiUsagePolicy(access);
}
}
[OPTIONAL] STEP 4: Define Decorator Parameters
If needed, you can define custom parameters for the annotation decorator.
Example Implementation:
import { Decorator } from "@semaver/reflector";
export function annotation(): DecoratorFn {
return Decorator.build(new AnnotationDecorator(1, "someString"));
}
export class AnnotationDecorator extends Decorator {
protected readonly myParam1: number;
protected readonly myParam2: string;
constructor(myParam1: number, myParam2: string) {
super();
this.myParam1 = myParam1;
this.myParam2 = myParam2;
}
public getParam1(): number {
return this.myParam1;
}
public getParam2(): string {
return this.myParam2;
}
/* override */
public getParameters(): ReadonlyArray<unknown> {
return [this.myParam1, this.myParam2];
}
}Use Annotation Decorators
Annotation decorators are applied similarly to typical TypeScript decorators using the @decorator syntax. However, the application of Decoration Policies can lead to significant differences in behavior.
For example, the @annotation decorator has an Access Policy set to MetadataAccessPolicy.PROPERTY, meaning the decorator is only allowed on class properties. In all other cases, it will be ignored.
export class MyClass extends MySuperClass {
// ALLOWED
@annotation()
public data: MyData;
// NOT ALLOWED BY ACCESS POLICY -> IGNORED
public constructor(@annotation() myOtherData: MyOtherData) {
super();
}
// NOT ALLOWED BY ACCESS POLICY -> IGNORED
@annotation()
public myMethod(myOtherData: MyOtherData) {
// method body
}
}Warning: Constructor Parameters: If you override a parent constructor in the child class, you must reapply decorators for constructor parameters. Overriding a parent constructor is treated similarly to defining a new method, meaning no policies from the parent constructor are applied. For a detailed explanation, refer to Annotation Decorators Usage for Constructor Parameters.
Decoration Policies
Decoration policies define the rules for how decorators behave in specific scenarios. Currently, there are two general policies and three class inheritance policies:
General Policies:
Class Inheritance Policies:
Access Policy
The MetadataAccessPolicy defines whether a decorator can be applied to a specific member, argument, or member group.
- Default:
MetadataAccessPolicy.ALL
//---PRIMITIVE ACCESS POLICY---------------------------------------------------------------------------
export interface PrimitiveMetadataAccessPolicyType {
NONE: number;
CONSTRUCTOR: number
INSTANCE_PROPERTY: number;
INSTANCE_ACCESSOR: number;
INSTANCE_METHOD: number;
STATIC_PROPERTY: number;
STATIC_ACCESSOR: number;
STATIC_METHOD: number;
PARAMETER_IN_CONSTRUCTOR: number;
PARAMETER_IN_INSTANCE_METHOD: number;
PARAMETER_IN_STATIC_METHOD: number;
}
export const PrimitiveMetadataAccessPolicy: Readonly<PrimitiveMetadataAccessPolicyType> = Object.freeze({
NONE: 0,
CONSTRUCTOR: 1,
INSTANCE_PROPERTY: 2,
STATIC_PROPERTY: 4,
INSTANCE_ACCESSOR: 8,
STATIC_ACCESSOR: 16,
INSTANCE_METHOD: 32,
STATIC_METHOD: 64,
PARAMETER_IN_CONSTRUCTOR: 128,
PARAMETER_IN_INSTANCE_METHOD: 256,
PARAMETER_IN_STATIC_METHOD: 512,
});
export type PrimitiveMetadataAccessPolicyValues =
PrimitiveMetadataAccessPolicyType[keyof PrimitiveMetadataAccessPolicyType];
//---ADVANCED ACCESS POLICY---------------------------------------------------------------------------
export interface MetadataAccessPolicyType extends PrimitiveMetadataAccessPolicyType {
ALL: number;
PROPERTY: number;
ACCESSOR: number;
METHOD: number;
PARAMETER: number
}
export const MetadataAccessPolicy: Readonly<MetadataAccessPolicyType> = Object.freeze({
NONE: PrimitiveMetadataAccessPolicy.NONE,
CONSTRUCTOR: PrimitiveMetadataAccessPolicy.CONSTRUCTOR,
INSTANCE_PROPERTY: PrimitiveMetadataAccessPolicy.INSTANCE_PROPERTY,
STATIC_PROPERTY: PrimitiveMetadataAccessPolicy.STATIC_PROPERTY,
PROPERTY: PrimitiveMetadataAccessPolicy.INSTANCE_PROPERTY |
PrimitiveMetadataAccessPolicy.STATIC_PROPERTY,
INSTANCE_ACCESSOR: PrimitiveMetadataAccessPolicy.INSTANCE_ACCESSOR,
STATIC_ACCESSOR: PrimitiveMetadataAccessPolicy.STATIC_ACCESSOR,
ACCESSOR: PrimitiveMetadataAccessPolicy.INSTANCE_ACCESSOR |
PrimitiveMetadataAccessPolicy.STATIC_ACCESSOR,
INSTANCE_METHOD: PrimitiveMetadataAccessPolicy.INSTANCE_METHOD,
STATIC_METHOD: PrimitiveMetadataAccessPolicy.STATIC_METHOD,
METHOD: PrimitiveMetadataAccessPolicy.INSTANCE_METHOD |
PrimitiveMetadataAccessPolicy.STATIC_METHOD,
PARAMETER_IN_CONSTRUCTOR: PrimitiveMetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR,
PARAMETER_IN_INSTANCE_METHOD: PrimitiveMetadataAccessPolicy.PARAMETER_IN_INSTANCE_METHOD,
PARAMETER_IN_STATIC_METHOD: PrimitiveMetadataAccessPolicy.PARAMETER_IN_STATIC_METHOD,
PARAMETER_IN_METHOD: PrimitiveMetadataAccessPolicy.PARAMETER_IN_INSTANCE_METHOD |
PrimitiveMetadataAccessPolicy.PARAMETER_IN_STATIC_METHOD,
PARAMETER: PrimitiveMetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR |
PrimitiveMetadataAccessPolicy.PARAMETER_IN_INSTANCE_METHOD |
PrimitiveMetadataAccessPolicy.PARAMETER_IN_STATIC_METHOD,
ALL: PrimitiveMetadataAccessPolicy.CONSTRUCTOR |
PrimitiveMetadataAccessPolicy.INSTANCE_PROPERTY |
PrimitiveMetadataAccessPolicy.STATIC_PROPERTY |
PrimitiveMetadataAccessPolicy.INSTANCE_ACCESSOR |
PrimitiveMetadataAccessPolicy.STATIC_ACCESSOR |
PrimitiveMetadataAccessPolicy.INSTANCE_METHOD |
PrimitiveMetadataAccessPolicy.STATIC_METHOD |
PrimitiveMetadataAccessPolicy.PARAMETER_IN_CONSTRUCTOR |
PrimitiveMetadataAccessPolicy.PARAMETER_IN_INSTANCE_METHOD |
PrimitiveMetadataAccessPolicy.PARAMETER_IN_STATIC_METHOD,
});
export type MetadataAccessPolicyValues = MetadataAccessPolicyType[keyof MetadataAccessPolicyType];
Same-Target-Multi-Usage Policy
The MetadataSameTargetMultiUsagePolicy defines the behavior when a class member or argument has more than one decorator of the same type:
ALLOWED: All decorators are registered.NOT_ALLOWED: Only the first (single) decorator is registered.- Default:
MetadataSameTargetMultiUsagePolicy.NOT_ALLOWED
export interface MetadataSameTargetMultiUsagePolicyType {
ALLOWED: number;
NOT_ALLOWED: number;
DEFAULT: number
}
export const MetadataSameTargetMultiUsagePolicy: Readonly<MetadataSameTargetMultiUsagePolicyType> = Object.freeze({
ALLOWED: 0,
NOT_ALLOWED: 1,
DEFAULT: 1,
});
export type MetadataSameTargetMultiUsagePolicyValues =
MetadataSameTargetMultiUsagePolicyType[keyof MetadataSameTargetMultiUsagePolicyType];
Collision Policy
The MetadataCollisionPolicy defines the behavior when a decorator exists in both the child class and its direct parent class for the same class member or argument:
SKIP: Both decorators (from the child and parent classes) are not used for the child class.OVERRIDE_CHILD: The parent class decorator is used for the child class.OVERRIDE_PARENT: The child class decorator is used for the child class.JOIN: Both decorators (from the child and parent classes) are used for the child class.THROW_ERROR: An error is thrown if a collision occurs.- Default:
MetadataCollisionPolicy.OVERRIDE_PARENT
export interface MetadataCollisionPolicyType {
OVERRIDE_PARENT: number;
OVERRIDE_CHILD: number;
THROW_ERROR: number;
JOIN: number;
SKIP: number;
DEFAULT: number
}