import "reflect-metadata" ; import { createClass , reflection } from "./helpers" ; import { getMetadata , setMetadata } from "./storage" ; import { Class , CustomPropertyDecorator , DecoratorId , DecoratorOption , DecoratorOptionId , DecoratorTargetType , GenericTypeParameterDecorator , GenericTypeArgumentDecorator , NativeParameterDecorator , NoopDecorator , ParameterPropertiesDecorator , PrivateDecorator , TypeDecorator , TypeOverride , } from "./types" ; /**
 * Add metadata information on parameter specified
 */ export function decorateParameter ( callback , option ? ) ; export function decorateParameter ( data , option ? ) ; export function decorateParameter ( data , option ? ) { return ( target , name , index ) => { const isCtorParam = reflection . isConstructor ( target ) ; const targetClass = isCtorParam ? target : target . constructor ; const methodName = isCtorParam ? "constructor" : name ; const meta = typeof data === "function" ? data ( targetClass , methodName , index ) : data ; setMetadata ( { ... meta , [ DecoratorOptionId ] : { ... meta [ DecoratorOptionId ] , ... option } } , targetClass , methodName , index ) ; } ; } /**
 * Add metadata information on method specified
 */ export function decorateMethod ( callback , option ? ) ; export function decorateMethod ( data , option ? ) ; export function decorateMethod ( data , option ? ) { return ( target , name ) => { const targetClass = target . constructor ; const meta = typeof data === "function" ? data ( targetClass , name ) : data ; setMetadata ( { ... meta , [ DecoratorOptionId ] : { ... meta [ DecoratorOptionId ] , ... option } } , targetClass , name ) ; } ; } /**
 * Add metadata information on property specified
 */ export function decorateProperty ( callback , option ? ) ; export function decorateProperty ( data , option ? ) ; export function decorateProperty ( data , option ? ) { return ( target , name , index ? ) => { if ( typeof index === "number" ) decorateParameter ( data , option ) ( target , name , index ) ; else decorateMethod ( data , option ) ( target , name , index ) ; } ; } /**
 * Add metadata information on class specified
 */ export function decorateClass ( callback , option ? ) ; export function decorateClass ( data , option ? ) ; export function decorateClass ( data , option ? ) { return ( target ) => { const meta = typeof data === "function" ? data ( target ) : data ; setMetadata ( { ... meta , [ DecoratorOptionId ] : { ... meta [ DecoratorOptionId ] , ... option } } , target ) ; } ; } /**
 * Add metadata information on specified declaration, can be applied to class, property, method, or parameter
 */ export function decorate ( data , targetTypes = [ ] , option ? ) { const throwIfNotOfType = ( target ) => { if ( targetTypes . length > 0 && ! targetTypes . some ( x => x === target ) ) throw new Error ( `Reflect Error: Decorator of type ${ targetTypes . join ( ", " ) } applied into ${ target }` ) ; } ; return ( ... args ) => { if ( args . length === 1 ) { throwIfNotOfType ( "Class" ) ; return decorateClass ( data , option ) ( args [ 0 ] ) ; } if ( args . length === 3 && typeof args [ 2 ] === "number" ) { throwIfNotOfType ( "Parameter" ) ; return decorateParameter ( data , option ) ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ; } if ( args [ 2 ] === undefined || args [ 2 ] . get || args [ 2 ] . set ) { throwIfNotOfType ( "Property" ) ; return decorateProperty ( data , option ) ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ; } throwIfNotOfType ( "Method" ) ; return decorateMethod ( data , option ) ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ; } ; } /**
 * Compose multiple metadata decorators into single decorator.
 *
 * ```
 * function merged() {
 *      return mergeDecorator(decoratorOne(), decoratorTwo(), decoratorThree())
 * }
 *
 * ＠merged()
 * class TargetClass {
 * }
 * ```
 */ export function mergeDecorator ( ... fn ) { return ( ... args ) => { fn . forEach ( x => ( x as Function ) ( ... args ) ) ; } ; } const symIgnore = Symbol ( "ignore" ) ; const symOverride = Symbol ( "override" ) ; const symParamProp = Symbol ( "paramProp" ) ; const symNoop = Symbol ( "noop" ) ; export function ignore ( ) { return decorate ( < PrivateDecorator > { [ DecoratorId ] : symIgnore , kind : "Ignore" } , [ "Parameter" , "Method" , "Property" ] , { allowMultiple : false } ) ; } param type param genericArguments export function type ( type , ... genericArguments ) { return decorate ( ( target ) => < TypeDecorator > { [ DecoratorId ] : symOverride , kind : "Override" , type , genericArguments : genericArguments , target } , [ "Parameter" , "Method" , "Property" ] , { inherit : true , allowMultiple : false } ) ; } /**
 * No operation decorator, this decorator does nothing except it use to force the TypeScript emit type information
 */ export function noop ( ) { return decorate ( ( target ) => < NoopDecorator > { [ DecoratorId ] : symNoop , kind : "Noop" , target } , undefined , { inherit : false , allowMultiple : false } ) ; } /**
 * Add metadata information that all parameters of specified class is a parameter properties
 */ export function parameterProperties ( ) { return decorateClass ( < ParameterPropertiesDecorator > { [ DecoratorId ] : symParamProp , type : "ParameterProperties" } , { allowMultiple : false } ) ; } export namespace generic { const symGenericType = Symbol ( "genericType" ) ; const symGenericTemplate = Symbol ( "genericTemplate" ) ; param parameters export function parameter ( ... parameters ) { return decorateClass ( target => < GenericTypeParameterDecorator > { [ DecoratorId ] : symGenericTemplate , kind : "GenericTemplate" , templates : parameters , target } , { inherit : false , allowMultiple : false } ) ; } param types export function argument ( ... types ) { return decorateClass ( target => < GenericTypeArgumentDecorator > { [ DecoratorId ] : symGenericType , kind : "GenericType" , types , target } , { inherit : false , allowMultiple : false } ) ; } param parent param params export function create < T extends Class > ( parent , ... params ) { const opt = ( typeof parent === "object" ) ? parent : { extends : parent , name : "DynamicType" } ; const Type = createClass ( { } , { ... opt , genericParams : params } ) ; return Type ; } param decorator param typeTarget returns export function getType ( decorator , typeTarget ) { const getParent = ( type ) : Class [ ] => { const parent = Object . getPrototypeOf ( type ) ; if ( type === decorator . target ) return [ ] ; return [ ... getParent ( parent ) , type ] ; } ; const getTemplate = ( target ) : GenericTypeParameterDecorator | undefined => { const meta = getMetadata ( target ) . find ( ( x ) : x is GenericTypeParameterDecorator => x . kind === "GenericTemplate" ) ; if ( meta ) return meta ; const parent = Object . getPrototypeOf ( target ) ; return parent . prototype ? getTemplate ( parent ) : undefined ; } ; if ( typeTarget === decorator . target ) return ; if ( ! ( typeTarget . prototype instanceof decorator . target ) ) throw new Error ( `Unable to get type information because ${ typeTarget . name } is not inherited from ${ decorator . target . name }` ) ; const [ type , isArray ] = reflection . getTypeFromDecorator ( decorator ) ; if ( typeof type !== "string" ) throw new Error ( "Provided decorator is not a generic type" ) ; let templateDec = getTemplate ( decorator . target ) ; if ( ! templateDec ) throw new Error ( `${ decorator . target . name } doesn't have @generic.parameter() decorator required by generic parameter ${ type }` ) ; const types = getParent ( typeTarget ) ; let tmpType = decorator . target ; let result = type ; for ( const type of types ) { const index = templateDec . templates . indexOf ( result ) ; const typeDec = getMetadata ( type ) . find ( ( x ) : x is GenericTypeArgumentDecorator => x . kind === "GenericType" ) ; if ( typeDec ) { if ( typeDec . types . length !== templateDec . templates . length ) throw new Error ( `Number of parameters @generic.parameter() and @generic.argument() mismatch between ${ tmpType . name } and ${ type . name }` ) ; result = typeDec . types [ index ] as any ; const finalResult = isArray ? [ result ] : result ; const singleResult = Array . isArray ( result ) ? result [ 0 ] : result ; if ( typeof singleResult === "function" ) return finalResult as any as ( Class | Class [ ] ) ; } const templates = getTemplate ( type ) ; tmpType = type ; templateDec = templates ! ; } } param type returns export function getArguments ( type ) { const genericDecorator = getMetadata ( type ) . find ( ( x ) : x is GenericTypeArgumentDecorator => x . kind == "GenericType" && x . target === type ) ; if ( ! genericDecorator ) { const parent = Object . getPrototypeOf ( type ) ; if ( ! parent . prototype ) throw new Error ( `${ type . name } is not a generic type` ) ; return getArguments ( parent ) ; } return genericDecorator . types . map ( x => x as Class ) ; } }