Last active
August 29, 2015 14:16
-
-
Save rbuckton/5903ff8a617f6afc5797 to your computer and use it in GitHub Desktop.
Example shim for es7decoratorunification API.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Reflect { | |
"use strict"; | |
const weakMetadata = new WeakMap<any, Map<any, any>>(); | |
const weakPropertyMetadata = new WeakMap<any, Map<PropertyKey, Map<any, any>>>(); | |
const weakParameterMetadata = new WeakMap<Function, Map<number, Map<any, any>>>(); | |
/** | |
* Applies a set of decorators to a target object. | |
* @param target The target object. | |
* @param decorators An array of decorators. | |
* @remarks Decorators are applied in reverse order. | |
*/ | |
export function decorate(target: Function, ...decorators: ((target: Function) => Function | void)[]): Function { | |
for (let i = decorators.length - 1; i >= 0; i--) { | |
let decorator = decorators[i]; | |
let decorated = decorator(target); | |
if (decorated === null || decorated === undefined) { | |
continue; | |
} | |
target = decorated; | |
} | |
return target; | |
} | |
/** | |
* Applies a set of decorators to a property of a target object. | |
* @param target The target object. | |
* @param propertyKey The property key to decorate. | |
* @param decorators An array of decorators. | |
* @remarks Decorators are applied in reverse order. | |
*/ | |
export function decorateProperty(target: any, propertyKey: PropertyKey, ...decorators: ((target: any, propertyKey: PropertyKey, descriptor: PropertyDescriptor) => PropertyDescriptor | void)[]): any { | |
let descriptor = Reflect.getOwnPropertyDescriptor(target, propertyKey); | |
if (descriptor === undefined) { | |
descriptor = { enumerable: true, configurable: true, writable: true }; | |
} | |
let { enumerable, configurable, writable, get, set, value } = descriptor; | |
for (let i = decorators.length - 1; i >= 0; i--) { | |
let decorator = decorators[i]; | |
let decorated = decorator(target, propertyKey, descriptor); | |
if (decorated === null || decorated === undefined) { | |
continue; | |
} | |
descriptor = decorated; | |
} | |
if (enumerable !== descriptor.enumerable || | |
configurable !== descriptor.configurable || | |
writable !== descriptor.writable || | |
value !== descriptor.value || | |
get !== descriptor.get || | |
set !== descriptor.set) { | |
Object.defineProperty(target, propertyKey, descriptor); | |
} | |
return target; | |
} | |
/** | |
* Applies a set of decorators to a function parameter. | |
* @param target The target function. | |
* @param parameterIndex The index of the parameter to decorate. | |
* @param decorators An array of decorators. | |
* @remarks Decorators are applied in reverse order. | |
*/ | |
export function decorateParameter(target: Function, parameterIndex: number, ...decorators: ((target: Function, paramterIndex: number) => void)[]): void { | |
for (let i = decorators.length - 1; i >= 0; i--) { | |
let decorator = decorators[i]; | |
decorator(target, parameterIndex); | |
} | |
} | |
/** | |
* Define a unique metadata entry on the target. | |
* @param target The target object on which to define metadata. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @param metadata A value that contains attached metadata. | |
* @example | |
* ``` | |
* // Component decorator factory as metadata-producing annotation. | |
* function Component(options) { | |
* return (target) => { Reflect.defineMetadata(target, Component, options); } | |
* } | |
* ``` | |
*/ | |
export function defineMetadata(target: any, metadataKey: any, metadata: any): void { | |
let metadataMap = weakMetadata.get(target); | |
if (!metadataMap) { | |
metadataMap = new Map<any, any>(); | |
weakMetadata.set(target, metadataMap); | |
} | |
metadataMap.set(metadataKey, metadata); | |
} | |
/** | |
* Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. | |
* @param target The target object on which the metadata is defined. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. | |
*/ | |
export function hasMetadata(target: any, metadataKey: any): boolean { | |
while (target) { | |
if (Reflect.hasOwnMetadata(target, metadataKey)) { | |
return true; | |
} | |
target = Reflect.getPrototypeOf(target); | |
} | |
return false; | |
} | |
/** | |
* Gets the metadata value for the provided metadata key on the target object or its prototype chain. | |
* @param target The target object on which the metadata is defined. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns The metadata value for the metadata key if found; otherwise, `undefined`. | |
* @example | |
* ``` | |
* let metadata = Reflect.getMetadata(target, Component); | |
* ``` | |
*/ | |
export function getMetadata(target: any, metadataKey: any): any { | |
while (target) { | |
if (Reflect.hasOwnMetadata(target, metadataKey)) { | |
return Reflect.getOwnMetadata(target, metadataKey); | |
} | |
target = Reflect.getPrototypeOf(target); | |
} | |
return undefined; | |
} | |
/** | |
* Gets the metadata keys defined on the target object or its prototype chain. | |
* @param target The target object on which the metadata is defined. | |
* @returns An array of unique metadata keys. | |
*/ | |
export function getMetadataKeys(target: any): any[] { | |
let keySet = new Set<any>(); | |
let keys: any[] = []; | |
while (target) { | |
for (let key of Reflect.getOwnMetadataKeys(target)) { | |
if (!keySet.has(key)) { | |
keySet.add(key); | |
keys.push(key); | |
} | |
} | |
target = Reflect.getPrototypeOf(target); | |
} | |
return keys; | |
} | |
/** | |
* Gets a value indicating whether the target object has the provided metadata key defined. | |
* @param target The target object on which the metadata is defined. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata key was defined on the target object; otherwise, `false`. | |
*/ | |
export function hasOwnMetadata(target: any, metadataKey: any): boolean { | |
let metadataMap = weakMetadata.get(target); | |
if (metadataMap) { | |
return metadataMap.has(metadataKey); | |
} | |
return false; | |
} | |
/** | |
* Gets the metadata value for the provided metadata key on the target object. | |
* @param target The target object on which the metadata is defined. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns The metadata value for the metadata key if found; otherwise, `undefined`. | |
* @example | |
* ``` | |
* let metadata = Reflect.getOwnMetadata(target, Component); | |
* ``` | |
*/ | |
export function getOwnMetadata(target: any, metadataKey: any): any { | |
let metadataMap = weakMetadata.get(target); | |
if (metadataMap) { | |
return metadataMap.get(metadataKey); | |
} | |
return undefined; | |
} | |
/** | |
* Gets the unique metadata keys defined on the target object. | |
* @param target The target object on which the metadata is defined. | |
* @returns An array of unique metadata keys. | |
*/ | |
export function getOwnMetadataKeys(target: any): any[] { | |
let metadataMap = weakMetadata.get(target); | |
if (metadataMap) { | |
return [...metadataMap.keys()]; | |
} | |
return []; | |
} | |
/** | |
* Deletes the metadata entry from the target object with the provided key. | |
* @param target The target object on which the metadata is defined. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata entry was found and deleted; otherwise, false. | |
*/ | |
export function deleteOwnMetadata(target: any, metadataKey: any): boolean { | |
let metadataMap = weakMetadata.get(target); | |
if (metadataMap) { | |
return metadataMap.delete(metadataKey); | |
} | |
return false; | |
} | |
/** | |
* Define a metadata entry on a property of the target. | |
* @param target The target object on which to define metadata. | |
* @param propertyKey The key of the property on the target. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @param metadata A value that contains attached metadata. | |
* @example | |
* ``` | |
* // MarshalAs decorator factory as metadata-producing annotation. | |
* function MarshalAs(options) { | |
* return (target, propertyKey) => { Reflect.definePropertyMetadata(target, propertyKey, MarshalAs, options); } | |
* } | |
* ``` | |
*/ | |
export function definePropertyMetadata(target: any, propertyKey: PropertyKey, metadataKey: any, metadata: any): void { | |
let propertyMap = weakPropertyMetadata.get(target); | |
if (!propertyMap) { | |
propertyMap = new Map<PropertyKey, Map<any, any>>(); | |
weakPropertyMetadata.set(target, propertyMap); | |
} | |
let metadataMap = propertyMap.get(propertyKey); | |
if (!metadataMap) { | |
metadataMap = new Map<any, any>(); | |
propertyMap.set(propertyKey, metadataMap); | |
} | |
metadataMap.set(metadataKey, metadata); | |
} | |
/** | |
* Gets a value indicating whether a property of the target object or its prototype chain has the provided metadata key defined. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata key was defined on a property of the target object or its prototype chain; otherwise, `false`. | |
*/ | |
export function hasPropertyMetadata(target: any, propertyKey: PropertyKey, metadataKey: any): boolean { | |
while (target) { | |
if (Reflect.hasOwnPropertyMetadata(target, propertyKey, metadataKey)) { | |
return true; | |
} | |
target = Reflect.getPrototypeOf(target); | |
} | |
return false; | |
} | |
/** | |
* Gets the first metadata value for the provided metadata key on a property of the target object or its prototype chain. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns The metadata value for the metadata key if found; otherwise, `undefined`. | |
* @example | |
* ``` | |
* let metadata = Reflect.getPropertyMetadata(target, propertyKey, MarshalAs); | |
* ``` | |
*/ | |
export function getPropertyMetadata(target: any, propertyKey: PropertyKey, metadataKey: any): any { | |
while (target) { | |
if (Reflect.hasOwnPropertyMetadata(target, propertyKey, metadataKey)) { | |
return Reflect.getOwnPropertyMetadata(target, propertyKey, metadataKey); | |
} | |
target = Reflect.getPrototypeOf(target); | |
} | |
return undefined; | |
} | |
/** | |
* Gets the metadata keys defined on a property of the target object or its prototype chain. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @returns An array of unique metadata keys. | |
*/ | |
export function getPropertyMetadataKeys(target: any, propertyKey: PropertyKey): any[] { | |
let keySet = new Set<any>(); | |
let keys: any[] = []; | |
while (target) { | |
for (let key of Reflect.getOwnPropertyMetadataKeys(target)) { | |
if (!keySet.has(key)) { | |
keySet.add(key); | |
keys.push(key); | |
} | |
} | |
target = Reflect.getPrototypeOf(target); | |
} | |
return keys; | |
} | |
/** | |
* Gets a value indicating whether a property of the target object has the provided metadata key defined. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata key was defined on the target object; otherwise, `false`. | |
*/ | |
export function hasOwnPropertyMetadata(target: any, propertyKey: PropertyKey, metadataKey: any): boolean { | |
let propertyMap = weakPropertyMetadata.get(target); | |
if (propertyMap) { | |
let metadataMap = propertyMap.get(propertyKey); | |
if (metadataMap) { | |
return metadataMap.has(metadataKey); | |
} | |
} | |
return false; | |
} | |
/** | |
* Gets the metadata value for the provided metadata key on a property of the target object. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns The metadata value for the metadata key if found; otherwise, `undefined`. | |
* @example | |
* ``` | |
* let metadata = Reflect.getOwnPropertyMetadata(target, propertyKey, MarshalAs); | |
* ``` | |
*/ | |
export function getOwnPropertyMetadata(target: any, propertyKey: PropertyKey, metadataKey: any): any { | |
let propertyMap = weakPropertyMetadata.get(target); | |
if (propertyMap) { | |
let metadataMap = propertyMap.get(propertyKey); | |
if (metadataMap) { | |
return metadataMap.get(metadataKey); | |
} | |
} | |
return undefined; | |
} | |
/** | |
* Gets the metadata keys defined on a property of the target object. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @returns An array of unique metadata keys. | |
*/ | |
export function getOwnPropertyMetadataKeys(target: any, propertyKey: PropertyKey): any[] { | |
let propertyMap = weakPropertyMetadata.get(target); | |
if (propertyMap) { | |
let metadataMap = propertyMap.get(propertyKey); | |
if (metadataMap) { | |
return [...metadataMap.keys()]; | |
} | |
} | |
return []; | |
} | |
/** | |
* Deletes the metadata from a property of the target object with the provided key. | |
* @param target The target object on which the metadata is defined. | |
* @param propertyKey The key of the property on the target. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata entry was found and deleted; otherwise, false. | |
*/ | |
export function deleteOwnPropertyMetadata(target: any, propertyKey: PropertyKey, metadataKey: any): boolean { | |
let propertyMap = weakPropertyMetadata.get(target); | |
if (propertyMap) { | |
let metadataMap = propertyMap.get(propertyKey); | |
if (metadataMap) { | |
return metadataMap.delete(metadataKey); | |
} | |
} | |
return false; | |
} | |
/** | |
* Define a metadata entry on a parameter of the target function. | |
* @param target The target function on which to define metadata. | |
* @param parameterIndex The ordinal parameter index. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @param metadata A value that contains attached metadata. | |
* @returns The target function. | |
* @example | |
* ``` | |
* // Inject decorator factory as metadata-producing annotation. | |
* function Inject(type) { | |
* return (target, parameterIndex) => { Reflect.defineMetadata(target, parameterIndex, Inject, type); } | |
* } | |
* ``` | |
*/ | |
export function defineParameterMetadata(target: Function, parameterIndex: number, metadataKey: any, metadata: any): void { | |
let parameterMap = weakParameterMetadata.get(target); | |
if (!parameterMap) { | |
parameterMap = new Map<number, Map<any, any>>(); | |
weakParameterMetadata.set(target, parameterMap); | |
} | |
let metadataMap = parameterMap.get(parameterIndex); | |
if (!metadataMap) { | |
metadataMap = new Map<any, any>(); | |
parameterMap.set(parameterIndex, metadataMap); | |
} | |
metadataMap.set(metadataKey, metadata); | |
} | |
/** | |
* Gets a value indicating whether a parameter of the target function or its prototype chain has the provided metadata key defined. | |
* @param target The target function on which the metadata is defined. | |
* @param parameterIndex The ordinal parameter index. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata key was defined on a property of the target function or its prototype chain; otherwise, `false`. | |
*/ | |
export function hasParameterMetadata(target: Function, parameterIndex: number, metadataKey: any): boolean { | |
let parameterMap = weakParameterMetadata.get(target); | |
if (parameterMap) { | |
let metadataMap = parameterMap.get(parameterIndex); | |
if (metadataMap) { | |
return metadataMap.has(metadataKey); | |
} | |
} | |
return false; | |
} | |
/** | |
* Gets the first occurance of metadata for the provided metadata key on a parameter of the target function. | |
* @param target The target function on which the metadata is defined. | |
* @param parameterIndex The ordinal parameter index. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns The metadata value for the metadata key if found; otherwise, `undefined`. | |
* @example | |
* ``` | |
* let metadata = Reflect.getParameterMetadata(target, parameterIndex, Inject); | |
* ``` | |
*/ | |
export function getParameterMetadata(target: Function, parameterIndex: number, metadataKey: any): any { | |
let parameterMap = weakParameterMetadata.get(target); | |
if (parameterMap) { | |
let metadataMap = parameterMap.get(parameterIndex); | |
if (metadataMap) { | |
return metadataMap.get(metadataKey); | |
} | |
} | |
return undefined; | |
} | |
/** | |
* Gets the unique metadata keys defined on a parameter of the target function. | |
* @param target The target function on which the metadata is defined. | |
* @param parameterIndex The ordinal parameter index. | |
* @returns An array of unique metadata keys. | |
*/ | |
export function getParameterMetadataKeys(target: Function, parameterIndex: number): any[] { | |
let parameterMap = weakParameterMetadata.get(target); | |
if (parameterMap) { | |
let metadataMap = parameterMap.get(parameterIndex); | |
if (metadataMap) { | |
return [...metadataMap.keys()]; | |
} | |
} | |
return []; | |
} | |
/** | |
* Deletes the metadata from a parameter of the target function with the provided key. | |
* @param target The target function on which the metadata is defined. | |
* @param parameterIndex The ordinal parameter index. | |
* @param metadataKey A key used to store and retrieve metadata. | |
* @returns `true` if the metadata entry was found and deleted; otherwise, false. | |
*/ | |
export function deleteParameterMetadata(target: Function, parameterIndex: number, metadataKey: any): boolean { | |
let parameterMap = weakParameterMetadata.get(target); | |
if (parameterMap) { | |
let metadataMap = parameterMap.get(parameterIndex); | |
if (metadataMap) { | |
return metadataMap.delete(metadataKey); | |
} | |
} | |
return undefined; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment