-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathutil.ts
213 lines (187 loc) · 7.14 KB
/
util.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// The code for makeDecorator is originally based on code from Angular 2.
import "reflect-metadata";
/**
* Represents a constructor function for a concrete type.
*/
export interface Constructor<T> {
/**
* The name of the type.
*/
name?: string;
/**
* Call signature for a constructor function.
* @param args Arguments for the constructor.
*/
new(...args: any[]): T;
}
/**
* Returns the list of annotations that are an instance of the specified annotation type.
* @param annotationCtr The constructor for the annotation
* @param annotations A list of annotations to filter
* @hidden
*/
export function matchingAnnotations<T>(annotationCtr: Constructor<T>, annotations: any[]): T[] {
if(annotationCtr) {
annotations = annotations.filter(annotation => annotation instanceof <any>annotationCtr);
}
return annotations;
}
/**
* Makes a decorator factory from an annotation. The resulting decorator factory can be applied to a class or property.
* @param annotationCtr The annotation constructor.
*
*
* ### Example
*
* An annotation is just a class. So, supposed with have an annotation that marks a class as a persistent entity as
* follows:
*
* ```typescript
* class EntityAnnotation {
*
* constructor(public name?: string) {
* }
* }
* ```
*
* We can turn that annotation into a class decorator. When applied, an instance of the annotation
* is created and added to the 'annotations' metadata for the class. The parameters for the decorator will be the same
* as the parameters for the class constructor. However, we may want to provide a type annotation for the decorator
* factory because the parameters cannot be inferred.
*
* ```typescript
* var Entity: (name?: string) => ClassDecorator = makeDecorator(EntityAnnotation);
* ```
*
* The Entity decorator can be applied as follows:
* ```typescript
* @Entity()
* class Person {
*
* }
* ```
*/
export function makeDecorator(annotationCtr: Constructor<any>) {
// The code for this function is adapted from https://github.com/angular/angular/blob/master/modules/angular2/src/core/util/decorators.ts
return function DecoratorFactory(...args: any[]) {
var annotationInstance = new annotationCtr(...args);
return function Decorator(target: Object, propertyName?: string, parameterIndex?: number): void {
// check for parameter annotation
if(typeof parameterIndex === "number") {
var parameters: any[][] = Reflect.getMetadata('parameters', target, propertyName);
parameters = parameters || [];
// there might be gaps if some in between parameters do not have annotations.
// we pad with nulls.
while (parameters.length <= parameterIndex) {
parameters.push(null);
}
parameters[parameterIndex] = parameters[parameterIndex] || [];
var annotationsForParam: any[] = parameters[parameterIndex];
annotationsForParam.push(annotationInstance);
Reflect.defineMetadata('parameters', parameters, target, propertyName);
}
// check for property/method annotation
else if(propertyName) {
var properties = Reflect.getOwnMetadata('propMetadata', target.constructor);
properties = properties || {};
properties[propertyName] = properties[propertyName] || [];
properties[propertyName].push(annotationInstance);
Reflect.defineMetadata('propMetadata', properties, target.constructor);
}
// otherwise, we have a class annotation
else {
var annotations = Reflect.getOwnMetadata('annotations', target) || [];
annotations.push(annotationInstance);
Reflect.defineMetadata('annotations', annotations, target);
}
}
}
}
/**
* A map of decorators to be applied to the properties of a type. The key is the name of the property and the value
* is a decorator or an array of decorators.
*/
export interface PropertyDecorators {
[x: string]: (PropertyDecorator | PropertyDecorator[]);
}
/**
* A helper function to apply decorators in ES5.
* @param target The constructor function or function that the decorators will be applied to.
* @param decorators A decorator or array of decorators to apply to the type or function.
* @param properties A map of decorators to be applied to the properties of a type. The key is the name of the property
* and the value is a decorator or an array of decorators.
*
* ### Basic example
*
* Suppose you have a decorator, Entity, that marks a class as a persistent entity. We can apply the decorator
* to our type as follows:
* ```javascript
* function Person(name) {
* this.name = name;
* }
*
* decorate(Person, Entity());
* ```
*
* An array of decorators can be applied to a type:
* ```javascript
* decorate(Person, [Entity(), Collection("people")]);
* ```
*
*
* ### Example with property decorators
*
* Suppose we have a decorator factory, Type, that gives the type of a field for a persistent entity. We can apply the
* decorator to a property of our type as follows:
* ```javascript
* function Person(name) {
* this.name = name;
* }
*
* decorate(Person, Entity(), {
* name: Type(String)
* });
* ```
*
* An array of decorators can be applied for a property:
* ```javascript
* decorate(Person, Entity(), {
* name: [Type(String), Field("n")]
* });
* ```
*/
export function decorate(target: Constructor<any> | Function, decorators: ClassDecorator | ClassDecorator[], properties?: PropertyDecorators): void;
export function decorate(target: Constructor<any> | Function, properties: { [x: string]: (PropertyDecorator | PropertyDecorator[])}): void;
export function decorate(target: Constructor<any> | Function, decoratorsOrProperties: PropertyDecorators | ClassDecorator | ClassDecorator[], properties?: PropertyDecorators): void {
var decorators: ClassDecorator[];
if(Array.isArray(decoratorsOrProperties)) {
decorators = decoratorsOrProperties;
}
if(typeof decoratorsOrProperties === "object") {
properties = <PropertyDecorators>decoratorsOrProperties;
}
else if(typeof decoratorsOrProperties === "function") {
decorators = [<ClassDecorator>decoratorsOrProperties];
}
if(decorators) {
Reflect.decorate(decorators, target);
}
if(properties) {
for(var p in properties) {
if(properties.hasOwnProperty(p)) {
if(!Array.isArray(properties[p])) {
var propDecorators = [<PropertyDecorator>properties[p]];
}
else {
var propDecorators = <PropertyDecorator[]>properties[p];
}
if(target.prototype[p] === "function") {
Reflect.decorate(propDecorators, target.prototype, p, Object.getOwnPropertyDescriptor(target.prototype, p));
}
else {
Reflect.decorate(propDecorators, target.prototype, p);
}
}
}
}
}