refactor(core): cleanup decorators.ts (#12161)

This commit is contained in:
Victor Berchet 2016-10-11 15:47:20 -07:00 committed by Tobias Bosch
parent 7275e1beb3
commit 7787771aba

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {global, isFunction, stringify} from '../facade/lang'; import {global, stringify} from '../facade/lang';
import {Type} from '../type'; import {Type} from '../type';
var _nextClassId = 0; let _nextClassId = 0;
const Reflect = global.Reflect;
/** /**
* Declares the interface to be used with {@link Class}. * Declares the interface to be used with {@link Class}.
@ -87,7 +88,7 @@ export interface TypeDecorator {
} }
function extractAnnotation(annotation: any): any { function extractAnnotation(annotation: any): any {
if (isFunction(annotation) && annotation.hasOwnProperty('annotation')) { if (typeof annotation === 'function' && annotation.hasOwnProperty('annotation')) {
// it is a decorator, extract annotation // it is a decorator, extract annotation
annotation = annotation.annotation; annotation = annotation.annotation;
} }
@ -99,13 +100,16 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
fnOrArray === Number || fnOrArray === Array) { fnOrArray === Number || fnOrArray === Array) {
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`); throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
} }
if (isFunction(fnOrArray)) {
return <Function>fnOrArray; if (typeof fnOrArray === 'function') {
} else if (fnOrArray instanceof Array) { return fnOrArray;
}
if (Array.isArray(fnOrArray)) {
const annotations: any[] = fnOrArray; const annotations: any[] = fnOrArray;
const annoLength = annotations.length - 1; const annoLength = annotations.length - 1;
const fn: Function = fnOrArray[annoLength]; const fn: Function = fnOrArray[annoLength];
if (!isFunction(fn)) { if (typeof fn !== 'function') {
throw new Error( throw new Error(
`Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`); `Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);
} }
@ -118,11 +122,11 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
const paramAnnotations: any[] = []; const paramAnnotations: any[] = [];
paramsAnnotations.push(paramAnnotations); paramsAnnotations.push(paramAnnotations);
const annotation = annotations[i]; const annotation = annotations[i];
if (annotation instanceof Array) { if (Array.isArray(annotation)) {
for (let j = 0; j < annotation.length; j++) { for (let j = 0; j < annotation.length; j++) {
paramAnnotations.push(extractAnnotation(annotation[j])); paramAnnotations.push(extractAnnotation(annotation[j]));
} }
} else if (isFunction(annotation)) { } else if (typeof annotation === 'function') {
paramAnnotations.push(extractAnnotation(annotation)); paramAnnotations.push(extractAnnotation(annotation));
} else { } else {
paramAnnotations.push(annotation); paramAnnotations.push(annotation);
@ -130,11 +134,11 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
} }
Reflect.defineMetadata('parameters', paramsAnnotations, fn); Reflect.defineMetadata('parameters', paramsAnnotations, fn);
return fn; return fn;
} else { }
throw new Error( throw new Error(
`Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`); `Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);
} }
}
/** /**
* Provides a way for expressing ES6 classes with parameter annotations in ES5. * Provides a way for expressing ES6 classes with parameter annotations in ES5.
@ -183,7 +187,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
* *
* ``` * ```
* var MyService = ng.Class({ * var MyService = ng.Class({
* constructor: [String, [new Query(), QueryList], function(name, queryList) { * constructor: [String, [new Optional(), Service], function(name, myService) {
* ... * ...
* }] * }]
* }); * });
@ -193,7 +197,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
* *
* ``` * ```
* class MyService { * class MyService {
* constructor(name: string, @Query() queryList: QueryList) { * constructor(name: string, @Optional() myService: Service) {
* ... * ...
* } * }
* } * }
@ -221,9 +225,11 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
export function Class(clsDef: ClassDefinition): Type<any> { export function Class(clsDef: ClassDefinition): Type<any> {
const constructor = applyParams( const constructor = applyParams(
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor'); clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
let proto = constructor.prototype; let proto = constructor.prototype;
if (clsDef.hasOwnProperty('extends')) { if (clsDef.hasOwnProperty('extends')) {
if (isFunction(clsDef.extends)) { if (typeof clsDef.extends === 'function') {
(<Function>constructor).prototype = proto = (<Function>constructor).prototype = proto =
Object.create((<Function>clsDef.extends).prototype); Object.create((<Function>clsDef.extends).prototype);
} else { } else {
@ -231,8 +237,9 @@ export function Class(clsDef: ClassDefinition): Type<any> {
`Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`); `Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);
} }
} }
for (let key in clsDef) { for (let key in clsDef) {
if (key != 'extends' && key != 'prototype' && clsDef.hasOwnProperty(key)) { if (key !== 'extends' && key !== 'prototype' && clsDef.hasOwnProperty(key)) {
proto[key] = applyParams(<any>clsDef[key], key); proto[key] = applyParams(<any>clsDef[key], key);
} }
} }
@ -249,10 +256,8 @@ export function Class(clsDef: ClassDefinition): Type<any> {
return <Type<any>>constructor; return <Type<any>>constructor;
} }
var Reflect = global.Reflect;
export function makeDecorator( export function makeDecorator(
name: string, props: {[key: string]: any}, parentClass?: any, name: string, props: {[name: string]: any}, parentClass?: any,
chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any { chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {
const metaCtor = makeMetadataCtor([props]); const metaCtor = makeMetadataCtor([props]);
@ -264,10 +269,11 @@ export function makeDecorator(
if (this instanceof DecoratorFactory) { if (this instanceof DecoratorFactory) {
metaCtor.call(this, objOrType); metaCtor.call(this, objOrType);
return this; return this;
} else { }
const annotationInstance = new (<any>DecoratorFactory)(objOrType); const annotationInstance = new (<any>DecoratorFactory)(objOrType);
const chainAnnotation = const chainAnnotation =
isFunction(this) && this.annotations instanceof Array ? this.annotations : []; typeof this === 'function' && Array.isArray(this.annotations) ? this.annotations : [];
chainAnnotation.push(annotationInstance); chainAnnotation.push(annotationInstance);
const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) { const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
const annotations = Reflect.getOwnMetadata('annotations', cls) || []; const annotations = Reflect.getOwnMetadata('annotations', cls) || [];
@ -280,10 +286,11 @@ export function makeDecorator(
if (chainFn) chainFn(TypeDecorator); if (chainFn) chainFn(TypeDecorator);
return TypeDecorator; return TypeDecorator;
} }
}
if (parentClass) { if (parentClass) {
DecoratorFactory.prototype = Object.create(parentClass.prototype); DecoratorFactory.prototype = Object.create(parentClass.prototype);
} }
DecoratorFactory.prototype.toString = () => `@${name}`; DecoratorFactory.prototype.toString = () => `@${name}`;
(<any>DecoratorFactory).annotationCls = DecoratorFactory; (<any>DecoratorFactory).annotationCls = DecoratorFactory;
return DecoratorFactory; return DecoratorFactory;
@ -295,12 +302,11 @@ function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any
const argVal = args[i]; const argVal = args[i];
if (Array.isArray(prop)) { if (Array.isArray(prop)) {
// plain parameter // plain parameter
const val = !argVal || argVal === undefined ? prop[1] : argVal; this[prop[0]] = !argVal || argVal === undefined ? prop[1] : argVal;
this[prop[0]] = val;
} else { } else {
for (let propName in prop) { for (let propName in prop) {
const val = !argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName]; this[propName] =
this[propName] = val; !argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName];
} }
} }
}); });
@ -309,7 +315,7 @@ function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any
} }
export function makeParamDecorator( export function makeParamDecorator(
name: string, props: ([string, any] | {[key: string]: any})[], parentClass?: any): any { name: string, props: ([string, any] | {[name: string]: any})[], parentClass?: any): any {
const metaCtor = makeMetadataCtor(props); const metaCtor = makeMetadataCtor(props);
function ParamDecoratorFactory(...args: any[]): any { function ParamDecoratorFactory(...args: any[]): any {
if (this instanceof ParamDecoratorFactory) { if (this instanceof ParamDecoratorFactory) {
@ -331,8 +337,7 @@ export function makeParamDecorator(
} }
parameters[index] = parameters[index] || []; parameters[index] = parameters[index] || [];
var annotationsForParam: any[] = parameters[index]; parameters[index].push(annotationInstance);
annotationsForParam.push(annotationInstance);
Reflect.defineMetadata('parameters', parameters, cls); Reflect.defineMetadata('parameters', parameters, cls);
return cls; return cls;
@ -353,8 +358,9 @@ export function makePropDecorator(
if (this instanceof PropDecoratorFactory) { if (this instanceof PropDecoratorFactory) {
metaCtor.apply(this, args); metaCtor.apply(this, args);
return this; return this;
} else { }
var decoratorInstance = new (<any>PropDecoratorFactory)(...args);
const decoratorInstance = new (<any>PropDecoratorFactory)(...args);
return function PropDecorator(target: any, name: string) { return function PropDecorator(target: any, name: string) {
const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {}; const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {};
meta[name] = meta[name] || []; meta[name] = meta[name] || [];
@ -362,7 +368,6 @@ export function makePropDecorator(
Reflect.defineMetadata('propMetadata', meta, target.constructor); Reflect.defineMetadata('propMetadata', meta, target.constructor);
}; };
} }
}
if (parentClass) { if (parentClass) {
PropDecoratorFactory.prototype = Object.create(parentClass.prototype); PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
} }