import { MetadataKeyType, PropertyKeyType, } from '../type'; import { TypeUtil } from '../util/TypeUtil'; export class Metadata { /** * Gets the metadata value for the provided metadata key on the target object or its prototype chain. * @param key A key used to store and retrieve metadata. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // constructor * result = Metadata.get("custom:annotation", Example); * * // property (on constructor) * result = Metadata.get("custom:annotation", Example, "staticProperty"); * * // property (on prototype) * result = Metadata.get("custom:annotation", Example.prototype, "property"); * * // method (on constructor) * result = Metadata.get("custom:annotation", Example, "staticMethod"); * * // method (on prototype) * result = Metadata.get("custom:annotation", Example.prototype, "method"); * ``` * */ public static get(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(key, TypeUtil.getType(target), propertyKey); } /** * Gets the metadata value for the provided metadata key on the target object or its prototype chain. * @param key A key used to store and retrieve metadata. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // constructor * result = Metadata.getOwn("custom:annotation", Example); * * // property (on constructor) * result = Metadata.getOwn("custom:annotation", Example, "staticProperty"); * * // property (on prototype) * result = Metadata.getOwn("custom:annotation", Example.prototype, "property"); * * // method (on constructor) * result = Metadata.getOwn("custom:annotation", Example, "staticMethod"); * * // method (on prototype) * result = Metadata.getOwn("custom:annotation", Example.prototype, "method"); * ``` * */ public static getOwn(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): any { return Reflect.getOwnMetadata(key, TypeUtil.getType(target), propertyKey); } /** * Gets the metadata value for the provided metadata DESIGN_TYPE on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.getType(Example); * * // property (on constructor) * result = Metadata.getType(Example, "staticProperty"); * * // method (on constructor) * result = Metadata.getType(Example, "staticMethod"); * ``` * */ public static getType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(DESIGN_TYPE, target, propertyKey); } /** * Gets the metadata value for the provided metadata DESIGN_TYPE on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.getOwnType(Example); * * // property (on constructor) * result = Metadata.getOwnType(Example, "staticProperty"); * * // method (on constructor) * result = Metadata.getOwnType(Example, "staticMethod"); * ``` * */ public static getOwnType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(DESIGN_TYPE, target, propertyKey); } /** * Gets the metadata value for the provided metadata DESIGN_RETURN_TYPE on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.getReturnType(Example); * * // property (on constructor) * result = Metadata.getReturnType(Example, "staticProperty"); * * // method (on constructor) * result = Metadata.getReturnType(Example, "staticMethod"); * ``` * */ public static getReturnType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(DESIGN_RETURN_TYPE, target, propertyKey); } /** * Gets the metadata value for the provided metadata DESIGN_RETURN_TYPE on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.getOwnReturnType(Example); * * // property (on constructor) * result = Metadata.getOwnReturnType(Example, "staticProperty"); * * // method (on constructor) * result = Metadata.getOwnReturnType(Example, "staticMethod"); * ``` * */ public static getOwnReturnType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getOwnMetadata(DESIGN_RETURN_TYPE, target, propertyKey); } /** * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. * @param key A key used to store and retrieve metadata. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // constructor * result = Metadata.has("custom:annotation", Example); * * // property (on constructor) * result = Metadata.has("custom:annotation", Example, "staticProperty"); * * // property (on prototype) * result = Metadata.has("custom:annotation", Example.prototype, "property"); * * // method (on constructor) * result = Metadata.has("custom:annotation", Example, "staticMethod"); * * // method (on prototype) * result = Metadata.has("custom:annotation", Example.prototype, "method"); * ``` * */ public static has(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): boolean { return Reflect.hasMetadata(key, TypeUtil.getType(target), propertyKey); } /** * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. * @param key A key used to store and retrieve metadata. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // constructor * result = Metadata.has("custom:annotation", Example); * * // property (on constructor) * result = Metadata.hasOwn("custom:annotation", Example, "staticProperty"); * * // property (on prototype) * result = Metadata.hasOwn("custom:annotation", Example.prototype, "property"); * * // method (on constructor) * result = Metadata.hasOwn("custom:annotation", Example, "staticMethod"); * * // method (on prototype) * result = Metadata.hasOwn("custom:annotation", Example.prototype, "method"); * ``` * */ public static hasOwn(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): boolean { return Reflect.hasOwnMetadata(key, TypeUtil.getType(target), propertyKey); } /** * Deletes the metadata entry from the target object with the provided key. * @param key A key used to store and retrieve metadata. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns `true` if the metadata entry was found and deleted; otherwise, false. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // constructor * result = Metadata.delete("custom:annotation", Example); * * // property (on constructor) * result = Metadata.delete("custom:annotation", Example, "staticProperty"); * * // property (on prototype) * result = Metadata.delete("custom:annotation", Example.prototype, "property"); * * // method (on constructor) * result = Metadata.delete("custom:annotation", Example, "staticMethod"); * * // method (on prototype) * result = Metadata.delete("custom:annotation", Example.prototype, "method"); * ``` * */ public static delete(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): boolean { return Reflect.deleteMetadata(key, TypeUtil.getType(target), propertyKey); } /** * Set the metadata value for the provided metadata DESIGN_PARAM_TYPES on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @param value A value that contains attached metadata. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.setParamTypes(Example, undefined, [Object]); * * // property (on constructor) * result = Metadata.setParamTypes(Example, "staticProperty", [Object]); * * // property (on prototype) * result = Metadata.setParamTypes(Example.prototype, "property", [Object]); * * // method (on constructor) * result = Metadata.setParamTypes(Example, "staticMethod", [Object]); * * // method (on prototype) * result = Metadata.setParamTypes(Example.prototype, "method", [Object]); * ``` * */ public static setParamTypes(target: any, propertyKey: PropertyKeyType, value: any): void { return this.set(DESIGN_PARAM_TYPES, value, target.prototype, propertyKey); } /** * Get all metadata for a metadataKey. * @param metadataKey */ public static getTargetsFromPropertyKey = (metadataKey: MetadataKeyType): any[] => PROPERTIES.has(metadataKey) ? PROPERTIES.get(metadataKey) || [] : [] /** * Define a unique metadata entry on the target. * @param key A key used to store and retrieve metadata. * @param value A value that contains attached metadata. * @param target The target object on which to define metadata. * @param propertyKey The property key for the target. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // constructor * Reflect.defineMetadata("custom:annotation", options, Example); * * // property (on constructor) * Reflect.defineMetadata("custom:annotation", Number, Example, "staticProperty"); * * // property (on prototype) * Reflect.defineMetadata("custom:annotation", Number, Example.prototype, "property"); * * // method (on constructor) * Reflect.defineMetadata("custom:annotation", Number, Example, "staticMethod"); * * // method (on prototype) * Reflect.defineMetadata("custom:annotation", Number, Example.prototype, "method"); * * // decorator factory as metadata-producing annotation. * function MyAnnotation(options): PropertyDecorator { * return (target, key) => Reflect.defineMetadata("custom:annotation", options, target, key); * } * ``` * */ public static set(key: MetadataKeyType, value: any, target: any, propertyKey?: PropertyKeyType): void { const targets: any[] = PROPERTIES.has(key) ? PROPERTIES.get(key) || [] : []; const classConstructor = TypeUtil.getType(target); if (targets.indexOf(classConstructor) === -1) { targets.push(classConstructor); PROPERTIES.set(key, targets); } Reflect.defineMetadata(key, value, TypeUtil.getType(target), propertyKey); } /** * Gets the metadata value for the provided metadata DESIGN_PARAM_TYPES on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.getParamTypes(Example); * * // property (on constructor) * result = Metadata.getParamTypes(Example, "staticProperty"); * * // method (on constructor) * result = Metadata.getParamTypes(Example, "staticMethod"); * ``` * */ public static getParamTypes(target: any, propertyKey?: PropertyKeyType): any[] { return Reflect.getMetadata(DESIGN_PARAM_TYPES, target, propertyKey); } /** * Gets the metadata value for the provided metadata DESIGN_PARAM_TYPES on the target object or its prototype chain. * @param target The target object on which the metadata is defined. * @param propertyKey The property key for the target. * @returns The metadata value for the metadata key if found; otherwise, `undefined`. * @example * * ```typescript * class Example { * // property declarations are not part of ES6, though they are valid in TypeScript: * // static staticProperty; * // property; * * static staticMethod(p) { } * method(p) { } * } * * // on contructor * result = Metadata.getParamTypes(Example); * * // property (on constructor) * result = Metadata.getParamTypes(Example, "staticProperty"); * * // method (on constructor) * result = Metadata.getParamTypes(Example, "staticMethod"); * ``` * */ public static getOwnParamTypes(target: any, propertyKey?: PropertyKeyType): any[] { return Reflect.getOwnMetadata(DESIGN_PARAM_TYPES, target, propertyKey); } } /** * Metadata key * @private * @type {string} */ const DESIGN_PARAM_TYPES = 'design:paramtypes'; /** * Metadata key * @private * @type {string} */ const DESIGN_TYPE = 'design:type'; /** * Metadata key * @private * @type {string} */ const DESIGN_RETURN_TYPE = 'design:returntype'; /** * Properties collections * @private * @type {string} */ const PROPERTIES: Map = new Map();