@dipscope/type-manager 3.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +653 -119
- package/core/discriminant.d.ts +8 -0
- package/core/discriminator.d.ts +6 -0
- package/core/fn.d.ts +21 -11
- package/core/generic-argument.d.ts +1 -1
- package/core/generic-metadata.d.ts +1 -1
- package/core/index.d.ts +8 -0
- package/core/index.js +2 -2
- package/core/inject-index.d.ts +6 -0
- package/core/inject-metadata.d.ts +15 -14
- package/core/inject-options.d.ts +5 -5
- package/core/log.d.ts +6 -6
- package/core/metadata.d.ts +3 -3
- package/core/property-metadata.d.ts +24 -36
- package/core/property-name.d.ts +6 -0
- package/core/property-options.d.ts +58 -2
- package/core/serializer-context-options.d.ts +6 -6
- package/core/serializer-context.d.ts +65 -6
- package/core/type-abstraction.d.ts +8 -0
- package/core/type-argument.d.ts +2 -2
- package/core/type-ctor.d.ts +1 -1
- package/core/type-fn.d.ts +8 -0
- package/core/type-like.d.ts +1 -1
- package/core/type-metadata-symbol.d.ts +6 -0
- package/core/type-metadata.d.ts +118 -28
- package/core/type-name.d.ts +6 -0
- package/core/type-options-base.d.ts +15 -8
- package/core/type-options.d.ts +21 -5
- package/core/type-resolver.d.ts +2 -2
- package/discriminant.d.ts +11 -0
- package/discriminator.d.ts +11 -0
- package/factories/index.js +2 -2
- package/factory.d.ts +4 -4
- package/index.d.ts +3 -3
- package/index.js +2 -2
- package/inject.d.ts +3 -3
- package/injector.d.ts +3 -3
- package/injectors/index.js +2 -2
- package/naming-conventions/index.js +2 -2
- package/naming-conventions/pascal-case.naming-convention.d.ts +1 -1
- package/package.json +1 -1
- package/preserve-discriminator.d.ts +10 -0
- package/property.d.ts +3 -3
- package/reference-handlers/index.js +2 -2
- package/reference-handlers/path.reference-handler.d.ts +22 -6
- package/serializers/index.js +2 -2
- package/type-manager-options.d.ts +3 -3
- package/type-manager.d.ts +217 -46
- package/inject-artisan.d.ts +0 -20
- package/property-artisan.d.ts +0 -20
- package/type-artisan.d.ts +0 -95
package/README.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
 
|
|
4
4
|
|
|
5
|
-
Type manager is a parsing package for TypeScript which will help you to transform JSON strings or plain objects into JavaScript object instances. It supports [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) or declarative configuration and allows you to configure parsing of your or 3rd party classes easily.
|
|
5
|
+
Type manager is a parsing package for `TypeScript` which will help you to transform JSON strings or plain objects into `JavaScript` object instances. It supports [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) or declarative configuration and allows you to configure parsing of your or 3rd party classes easily.
|
|
6
|
+
|
|
7
|
+
We recommend to use our [official website](https://dipscope.com/type-manager/what-issues-it-solves) to navigate through available features. You can also use the latest documentation described below.
|
|
8
|
+
|
|
9
|
+
## Give a star :star:
|
|
10
|
+
|
|
11
|
+
If you like or are using this project please give it a star. Thanks!
|
|
6
12
|
|
|
7
13
|
## Table of contents
|
|
8
14
|
|
|
@@ -16,17 +22,24 @@ Type manager is a parsing package for TypeScript which will help you to transfor
|
|
|
16
22
|
* [Defining helper decorators](#defining-helper-decorators)
|
|
17
23
|
* [Alias decorator](#alias-decorator)
|
|
18
24
|
* [Custom data decorator](#custom-data-decorator)
|
|
25
|
+
* [Default value decorator](#default-value-decorator)
|
|
26
|
+
* [Deserializable decorator](#deserializable-decorator)
|
|
27
|
+
* [Discriminant decorator](#discriminant-decorator)
|
|
28
|
+
* [Discriminator decorator](#discriminator-decorator)
|
|
19
29
|
* [Factory decorator](#factory-decorator)
|
|
20
|
-
* [Serializer decorator](#serializer-decorator)
|
|
21
|
-
* [Injector decorator](#injector-decorator)
|
|
22
30
|
* [Injectable decorator](#injectable-decorator)
|
|
23
|
-
* [
|
|
24
|
-
* [
|
|
31
|
+
* [Injector decorator](#injector-decorator)
|
|
32
|
+
* [Naming convention decorator](#naming-convention-decorator)
|
|
33
|
+
* [Preserve discriminator decorator](#preserve-discriminator-decorator)
|
|
34
|
+
* [Reference handler decorator](#reference-handler-decorator)
|
|
35
|
+
* [Serializable decorator](#serializable-decorator)
|
|
36
|
+
* [Serializer decorator](#serializer-decorator)
|
|
25
37
|
* [Use default value decorator](#use-default-value-decorator)
|
|
26
38
|
* [Use implicit conversion decorator](#use-implicit-conversion-decorator)
|
|
27
39
|
* [Defining configuration manually](#defining-configuration-manually)
|
|
28
40
|
* [Configuring global options](#configuring-global-options)
|
|
29
41
|
* [Configuring options per type](#configuring-options-per-type)
|
|
42
|
+
* [Configuring usage of polymorphic types](#configuring-usage-of-polymorphic-types)
|
|
30
43
|
* [Configuring naming convention](#configuring-naming-convention)
|
|
31
44
|
* [Configuring reference handler](#configuring-reference-handler)
|
|
32
45
|
* [Advanced usage](#advanced-usage)
|
|
@@ -37,17 +50,19 @@ Type manager is a parsing package for TypeScript which will help you to transfor
|
|
|
37
50
|
* [Defining custom naming convention](#defining-custom-naming-convention)
|
|
38
51
|
* [Use cases](#use-cases)
|
|
39
52
|
* [Built in serializers](#built-in-serializers)
|
|
40
|
-
* [
|
|
53
|
+
* [Circular object references](#circular-object-references)
|
|
41
54
|
* [Dependency injection and immutable types](#dependency-injection-and-immutable-types)
|
|
42
55
|
* [Different case usage in class and JSON](#different-case-usage-in-class-and-json)
|
|
43
|
-
* [
|
|
56
|
+
* [Enum types](#enum-types)
|
|
57
|
+
* [Generic types](#generic-types)
|
|
44
58
|
* [Integration with Angular](#integration-with-angular)
|
|
59
|
+
* [Polymorphic types](#polymorphic-types)
|
|
45
60
|
* [Notes](#notes)
|
|
46
61
|
* [License](#license)
|
|
47
62
|
|
|
48
63
|
## What issues it solves?
|
|
49
64
|
|
|
50
|
-
In JavaScript there are two types of objects:
|
|
65
|
+
In `JavaScript` there are two types of objects:
|
|
51
66
|
|
|
52
67
|
* Plain objects which are created using `{}` notation;
|
|
53
68
|
* Constructor based objects which are created using `new Class()` notation.
|
|
@@ -85,7 +100,7 @@ export class User
|
|
|
85
100
|
|
|
86
101
|
public constructor(id: number, name: string)
|
|
87
102
|
{
|
|
88
|
-
this.id
|
|
103
|
+
this.id = id;
|
|
89
104
|
this.name = name;
|
|
90
105
|
|
|
91
106
|
return;
|
|
@@ -118,7 +133,7 @@ Do you see the problem in this piece of code?
|
|
|
118
133
|
|
|
119
134
|
So what to do? How to get `User` array from our JSON?
|
|
120
135
|
|
|
121
|
-
Solution is to create new instances of `User` class from plain objects returned by `JSON.parse` function. But things may go wrong once you have a more complex object hierarchy. Besides `deletedAt` property is represented as `
|
|
136
|
+
Solution is to create new instances of `User` class from plain objects returned by `JSON.parse` function. But things may go wrong once you have a more complex object hierarchy. Besides `deletedAt` property is represented as `String` in JSON but `User` class declares it as a `Date` so we also have to perform appropriate conversion when assigning this property.
|
|
122
137
|
|
|
123
138
|
There exists much more simple way. Let's use our `TypeManager` for getting instances of `User` class from JSON:
|
|
124
139
|
|
|
@@ -136,7 +151,7 @@ for (const user of users)
|
|
|
136
151
|
}
|
|
137
152
|
```
|
|
138
153
|
|
|
139
|
-
Now we can use all power provided by JavaScript class instances. Want to know more? Let's dive into the details.
|
|
154
|
+
Now we can use all power provided by `JavaScript` class instances. Want to know more? Let's dive into the details.
|
|
140
155
|
|
|
141
156
|
## Installation
|
|
142
157
|
|
|
@@ -155,33 +170,54 @@ _This package has no dependencies. If you want additional type-safety and reduce
|
|
|
155
170
|
It defines configuration for each object which you are going to serialize or deserialize and uses this configuration to process data of your choice. There are two possible ways to define a configuration:
|
|
156
171
|
|
|
157
172
|
* Using decorator annotations;
|
|
158
|
-
* Using declarative
|
|
173
|
+
* Using declarative style;
|
|
159
174
|
|
|
160
175
|
The first one is the easiest and can be used for any class you control. If you want to configure serialization of 3rd party classes, global options or you don't like decorators it is better to go with the second. There are no restrictions to use one or another. You can combine two ways of configuration depending on which one fits better.
|
|
161
176
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
We have plenty of decorators. Each of them will be described in details but let's start from the most simple example of configuration.
|
|
177
|
+
Let's have a look at the most simple example of configuration using decorators.
|
|
165
178
|
|
|
166
179
|
```typescript
|
|
167
|
-
import 'reflect-metadata';
|
|
168
180
|
import { Type, Property } from '@dipscope/type-manager';
|
|
169
181
|
|
|
170
182
|
@Type()
|
|
171
183
|
export class User
|
|
172
184
|
{
|
|
173
|
-
@Property() public name: string;
|
|
174
|
-
@Property() public email: string;
|
|
185
|
+
@Property(String) public name: string;
|
|
186
|
+
@Property(String) public email: string;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Here we have a `User` class with `Type` and `Property` decorators assigned to it. `Type` decorator declares a type. `Property` decorator describes available properties for that type.
|
|
191
|
+
|
|
192
|
+
The same configuration can be rewritten using declarative style.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { TypeManager } from '@dipscope/type-manager';
|
|
196
|
+
import { PropertyName, PropertyOptions } from '@dipscope/type-manager/core';
|
|
197
|
+
|
|
198
|
+
export class User
|
|
199
|
+
{
|
|
200
|
+
public name: string;
|
|
201
|
+
public email: string;
|
|
175
202
|
}
|
|
203
|
+
|
|
204
|
+
TypeManager.configureTypeOptions(User, {
|
|
205
|
+
propertyOptionsMap: new Map<PropertyName, PropertyOptions<any>>([
|
|
206
|
+
['name', { typeArgument: String }],
|
|
207
|
+
['email', { typeArgument: String }],
|
|
208
|
+
])
|
|
209
|
+
});
|
|
176
210
|
```
|
|
177
211
|
|
|
178
|
-
|
|
212
|
+
As you can see now our `User` class defined without decorators. Instead you have to call `TypeManager` configure method and provide `TypeOptions` related to `User` type.
|
|
213
|
+
|
|
214
|
+
No matter what style of configuration you have chosen the next step is to call serialize methods of `TypeManager` with providing a type and data you want to process.
|
|
179
215
|
|
|
180
216
|
```typescript
|
|
181
217
|
import { TypeManager } from '@dipscope/type-manager';
|
|
182
218
|
|
|
183
219
|
const userObject = TypeManager.serialize(User, new User());
|
|
184
|
-
const user
|
|
220
|
+
const user = TypeManager.deserialize(User, userObject);
|
|
185
221
|
|
|
186
222
|
user instanceof User; // True.
|
|
187
223
|
```
|
|
@@ -192,26 +228,30 @@ Calling serialize creates a plain object and deserialize creates an instance of
|
|
|
192
228
|
import { TypeManager } from '@dipscope/type-manager';
|
|
193
229
|
|
|
194
230
|
const userJson = TypeManager.stringify(User, new User());
|
|
195
|
-
const user
|
|
231
|
+
const user = TypeManager.parse(User, userJson);
|
|
196
232
|
|
|
197
233
|
user instanceof User; // True.
|
|
198
234
|
```
|
|
199
235
|
|
|
200
|
-
Stringify and parse functions are wrappers over native JSON class functions. In addition they add serialize and deserialize support under the hood.
|
|
236
|
+
Stringify and parse functions are wrappers over native JSON class functions. In addition they add serialize and deserialize support under the hood.
|
|
201
237
|
|
|
202
|
-
Static functions are not the only way to work with a `TypeManager`. You can also work on instance based manner.
|
|
238
|
+
Static functions are not the only way to work with a `TypeManager`. You can also work on instance based manner.
|
|
203
239
|
|
|
204
240
|
```typescript
|
|
205
241
|
import { TypeManager } from '@dipscope/type-manager';
|
|
206
242
|
|
|
207
243
|
const userManager = new TypeManager(User);
|
|
208
|
-
const userObject
|
|
209
|
-
const user
|
|
244
|
+
const userObject = userManager.serialize(new User());
|
|
245
|
+
const user = userManager.deserialize(userObject);
|
|
210
246
|
|
|
211
247
|
user instanceof User; // True.
|
|
212
248
|
```
|
|
213
249
|
|
|
214
|
-
|
|
250
|
+
At first glance, it may seems that there is no difference but creating an instance of `TypeManager` preserves a configuration state. You can work with different configurations at the same time by providing type manager options as a second optional constructor argument. If it is not required then it is better to use static methods. They are not invoking state control which results in better performance.
|
|
251
|
+
|
|
252
|
+
## Defining decorators
|
|
253
|
+
|
|
254
|
+
We have plenty of decorators but there are only a few which controls the main flow. This are `Type`, `Property` and `Inject` decorators. Let's go through each of them.
|
|
215
255
|
|
|
216
256
|
### Type decorator
|
|
217
257
|
|
|
@@ -242,13 +282,13 @@ export class User
|
|
|
242
282
|
}
|
|
243
283
|
```
|
|
244
284
|
|
|
245
|
-
This call defines a type alias which can be later used to resolve a type for a property at runtime. We will talk about details in the property decorator section. Also we defined custom serializer for a type which is an implementation of `Serializer` interface. This serializer will be used later to serialize and deserialize a type including all custom logic of your choice. You can read more about [creating a custom serializer](#defining-custom-serializer) in
|
|
285
|
+
This call defines a type alias which can be later used to resolve a type for a property at runtime. We will talk about details in the property decorator section. Also we defined custom serializer for a type which is an implementation of `Serializer` interface. This serializer will be used later to serialize and deserialize a type including all custom logic of your choice. You can read more about [creating a custom serializer](#defining-custom-serializer) in a separate section.
|
|
246
286
|
|
|
247
287
|
There are more options can be provided for a type, so check `TypeOptions` definition or section with [helper decorators](#defining-helper-decorators) below.
|
|
248
288
|
|
|
249
289
|
### Property decorator
|
|
250
290
|
|
|
251
|
-
Property decorator defines per property configuration within a type and should be declared right before a property definition.
|
|
291
|
+
Property decorator defines per property configuration within a type and should be declared right before a property or accessor definition.
|
|
252
292
|
|
|
253
293
|
```typescript
|
|
254
294
|
import 'reflect-metadata';
|
|
@@ -261,7 +301,7 @@ export class User
|
|
|
261
301
|
}
|
|
262
302
|
```
|
|
263
303
|
|
|
264
|
-
This will register a `name` property for a `User`. Each property has a type associated with it. In our case this is a `
|
|
304
|
+
This will register a `name` property for a `User`. Each property has a type associated with it. In our case this is a `String`. By default if no configure options are provided decorator will try to resolve a property type using [reflect-metadata](https://github.com/rbuckton/reflect-metadata). If you are not using reflect metadata then such configuration will result a property type to be `unknown` and you will get an error during serialization. For such a case you have to explicitly define a property type.
|
|
265
305
|
|
|
266
306
|
```typescript
|
|
267
307
|
import { Type, Property } from '@dipscope/type-manager';
|
|
@@ -302,7 +342,7 @@ export class User
|
|
|
302
342
|
|
|
303
343
|
This option configures an alias so `username` property will be used instead of `name` when deserializing from object. There are plenty of configure options, so check `PropertyOptions` definition or section with [helper decorators](#defining-helper-decorators) below. For example you can make some properties serializable only or define custom property serialization.
|
|
304
344
|
|
|
305
|
-
Now let's have a look at more complex definitions with generic types.
|
|
345
|
+
Now let's have a look at more complex definitions with generic types. This are `Array<TType>`, `Map<TKey, TValue>` and others. To declare one of this you have to use extra argument available for `Property` decorator. Generic arguments are always passed as array to exactly see them within a source code.
|
|
306
346
|
|
|
307
347
|
If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) then provide generics as a first argument so configure options will become the second.
|
|
308
348
|
|
|
@@ -329,9 +369,28 @@ export class User
|
|
|
329
369
|
}
|
|
330
370
|
```
|
|
331
371
|
|
|
332
|
-
This is
|
|
372
|
+
This is a full set of arguments available for the property. Basically when using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) you have just to omit the first argument.
|
|
333
373
|
|
|
334
|
-
|
|
374
|
+
We try to simplify declarations as much as possible so you are free to use only configure options if you want and don't ever think about `Property` decorator arguments.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
378
|
+
|
|
379
|
+
@Type()
|
|
380
|
+
export class User
|
|
381
|
+
{
|
|
382
|
+
@Property({
|
|
383
|
+
typeArgument: Map,
|
|
384
|
+
genericArguments: [String, Number],
|
|
385
|
+
alias: 'myMap'
|
|
386
|
+
})
|
|
387
|
+
public map: Map<string, number>;
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Which syntax to use is completely on your choice. `Property` decorator is smart enough to setup everything based on usage.
|
|
392
|
+
|
|
393
|
+
Now let's talk a bit about relation types. They are not differ from built in types, so if you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) the definition can be the following.
|
|
335
394
|
|
|
336
395
|
```typescript
|
|
337
396
|
import 'reflect-metadata';
|
|
@@ -340,14 +399,12 @@ import { Type, Property } from '@dipscope/type-manager';
|
|
|
340
399
|
@Type()
|
|
341
400
|
export class UserStatus
|
|
342
401
|
{
|
|
343
|
-
@Property() public name: string;
|
|
344
402
|
@Property() public title: string;
|
|
345
403
|
}
|
|
346
404
|
|
|
347
405
|
@Type()
|
|
348
406
|
export class User
|
|
349
407
|
{
|
|
350
|
-
@Property() public name: string;
|
|
351
408
|
@Property() public userStatus: UserStatus;
|
|
352
409
|
}
|
|
353
410
|
```
|
|
@@ -361,14 +418,12 @@ import { Type, Property } from '@dipscope/type-manager';
|
|
|
361
418
|
@Type()
|
|
362
419
|
export class UserStatus
|
|
363
420
|
{
|
|
364
|
-
@Property() public name: string;
|
|
365
421
|
@Property() public title: string;
|
|
366
422
|
}
|
|
367
423
|
|
|
368
424
|
@Type()
|
|
369
425
|
export class User
|
|
370
426
|
{
|
|
371
|
-
@Property() public name: string;
|
|
372
427
|
@Property([UserStatus]) public userStatuses: UserStatus[];
|
|
373
428
|
}
|
|
374
429
|
```
|
|
@@ -381,14 +436,12 @@ import { Type, Property } from '@dipscope/type-manager';
|
|
|
381
436
|
@Type()
|
|
382
437
|
export class UserStatus
|
|
383
438
|
{
|
|
384
|
-
@Property(String) public name: string;
|
|
385
439
|
@Property(String) public title: string;
|
|
386
440
|
}
|
|
387
441
|
|
|
388
442
|
@Type()
|
|
389
443
|
export class User
|
|
390
444
|
{
|
|
391
|
-
@Property(String) public name: string;
|
|
392
445
|
@Property(UserStatus) public userStatus: UserStatus;
|
|
393
446
|
}
|
|
394
447
|
```
|
|
@@ -401,14 +454,12 @@ import { Type, Property } from '@dipscope/type-manager';
|
|
|
401
454
|
@Type()
|
|
402
455
|
export class UserStatus
|
|
403
456
|
{
|
|
404
|
-
@Property(String) public name: string;
|
|
405
457
|
@Property(String) public title: string;
|
|
406
458
|
}
|
|
407
459
|
|
|
408
460
|
@Type()
|
|
409
461
|
export class User
|
|
410
462
|
{
|
|
411
|
-
@Property(String) public name: string;
|
|
412
463
|
@Property(Array, [UserStatus]) public userStatuses: UserStatus[];
|
|
413
464
|
}
|
|
414
465
|
```
|
|
@@ -423,14 +474,12 @@ import { Type, Property } from '@dipscope/type-manager';
|
|
|
423
474
|
})
|
|
424
475
|
export class UserStatus
|
|
425
476
|
{
|
|
426
|
-
@Property(String) public name: string;
|
|
427
477
|
@Property(String) public title: string;
|
|
428
478
|
}
|
|
429
479
|
|
|
430
480
|
@Type()
|
|
431
481
|
export class User
|
|
432
482
|
{
|
|
433
|
-
@Property(String) public name: string;
|
|
434
483
|
@Property('UserStatus') public userStatus: UserStatus;
|
|
435
484
|
}
|
|
436
485
|
```
|
|
@@ -455,6 +504,65 @@ export class UserStatus
|
|
|
455
504
|
|
|
456
505
|
One great thing to know about arguments for property type and generics is that you can pass lazy function, type directly or type alias. Which definition to use is completely on your choice and dependent from certain use cases.
|
|
457
506
|
|
|
507
|
+
While property type arguments exactly match to `TypeScript` types there is a one exception for this rule. This is `Enum`. You have to provide `String` type for a string based `Enum` and `Number` type for a number based `Enum`. This is because of how `Enum` is represented after compiling it to `JavaScript`. You can read more about this [here](https://www.typescriptlang.org/docs/handbook/enums.html).
|
|
508
|
+
|
|
509
|
+
If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) this will be done automatically so no additional steps are required from your side.
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
import 'reflect-metadata';
|
|
513
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
514
|
+
|
|
515
|
+
export enum UserPriorityNumeric
|
|
516
|
+
{
|
|
517
|
+
Low,
|
|
518
|
+
Medium,
|
|
519
|
+
High
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export enum UserPriorityTextual
|
|
523
|
+
{
|
|
524
|
+
Low = 'Low',
|
|
525
|
+
Medium = 'Medium',
|
|
526
|
+
High = 'High'
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
@Type()
|
|
530
|
+
export class User
|
|
531
|
+
{
|
|
532
|
+
@Property() public userPriorityNumeric: UserPriorityNumeric;
|
|
533
|
+
@Property() public userPriorityTextual: UserPriorityTextual;
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
If types defined explicitly then definition will be the following.
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
541
|
+
|
|
542
|
+
export enum UserPriorityNumeric
|
|
543
|
+
{
|
|
544
|
+
Low,
|
|
545
|
+
Medium,
|
|
546
|
+
High
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export enum UserPriorityTextual
|
|
550
|
+
{
|
|
551
|
+
Low = 'Low',
|
|
552
|
+
Medium = 'Medium',
|
|
553
|
+
High = 'High'
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
@Type()
|
|
557
|
+
export class User
|
|
558
|
+
{
|
|
559
|
+
@Property(Number) public userPriorityNumeric: UserPriorityNumeric;
|
|
560
|
+
@Property(String) public userPriorityTextual: UserPriorityTextual;
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
One should remember this when explicitly defining types for enums.
|
|
565
|
+
|
|
458
566
|
### Inject decorator
|
|
459
567
|
|
|
460
568
|
Inject decorator controls your type dependency and declared right before a constructor parameter.
|
|
@@ -491,11 +599,11 @@ import { Injectable } from '@dipscope/type-manager/helpers';
|
|
|
491
599
|
@Injectable()
|
|
492
600
|
export class UserService
|
|
493
601
|
{
|
|
494
|
-
public
|
|
602
|
+
public property: string;
|
|
495
603
|
}
|
|
496
604
|
```
|
|
497
605
|
|
|
498
|
-
In most cases you will work in environment where dependency injection system is already setted up. In this case you have to implement custom `Injector` to be used instead of our default one. Besides you should follow the steps to register injectable services specified by the vendor. This means that you should not use `Injectable` decorator from our package. You can read more about [creating a custom injector](#defining-custom-injector) in
|
|
606
|
+
In most cases you will work in environment where dependency injection system is already setted up. In this case you have to implement custom `Injector` to be used instead of our default one. Besides you should follow the steps to register injectable services specified by the vendor. This means that you should not use `Injectable` decorator from our package. You can read more about [creating a custom injector](#defining-custom-injector) in a separate section.
|
|
499
607
|
|
|
500
608
|
If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) the injection of services can be simplified.
|
|
501
609
|
|
|
@@ -524,7 +632,7 @@ Note that now you don't have to specify injection for types explicitly. However
|
|
|
524
632
|
|
|
525
633
|
### Alias decorator
|
|
526
634
|
|
|
527
|
-
|
|
635
|
+
This decorator can be used both on type and property to define an alias.
|
|
528
636
|
|
|
529
637
|
```typescript
|
|
530
638
|
import { Type, Property, Alias } from '@dipscope/type-manager';
|
|
@@ -557,7 +665,7 @@ Alias defined for a property declares that property name differs from one specif
|
|
|
557
665
|
|
|
558
666
|
### Custom data decorator
|
|
559
667
|
|
|
560
|
-
|
|
668
|
+
This decorator can be used to provide any custom data for type or property.
|
|
561
669
|
|
|
562
670
|
```typescript
|
|
563
671
|
import { Type, Property, CustomData } from '@dipscope/type-manager';
|
|
@@ -584,17 +692,76 @@ export class User
|
|
|
584
692
|
}
|
|
585
693
|
```
|
|
586
694
|
|
|
587
|
-
This custom data later can be accessed in serializers, factories or
|
|
695
|
+
This custom data later can be accessed in serializers, factories, injectors or your code and used accordingly. Read more about [defining custom data](#defining-custom-data) in a separate section.
|
|
588
696
|
|
|
589
|
-
###
|
|
697
|
+
### Default value decorator
|
|
590
698
|
|
|
591
|
-
|
|
699
|
+
This decorator is used to define a default value when one is undefined. It can be used on type or property.
|
|
592
700
|
|
|
593
701
|
```typescript
|
|
594
|
-
import { Type, Property,
|
|
702
|
+
import { Type, Property, DefaultValue } from '@dipscope/type-manager';
|
|
595
703
|
|
|
596
704
|
@Type()
|
|
597
|
-
@
|
|
705
|
+
@DefaultValue(() => new User())
|
|
706
|
+
export class User
|
|
707
|
+
{
|
|
708
|
+
@Property(String) @DefaultValue('BestName') public name: string;
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
Such declaration is an alternative for:
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
716
|
+
|
|
717
|
+
@Type({
|
|
718
|
+
defaultValue: () => new User()
|
|
719
|
+
})
|
|
720
|
+
export class User
|
|
721
|
+
{
|
|
722
|
+
@Property(String, { defaultValue: 'BestName' }) public name: string;
|
|
723
|
+
}
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
As you can see it accepts an arrow function or a certain value. Both are valid for type and property. Using default values is turned off by default. You can enable them using `UseDefaultValue` decorator per type and property or enable globally using `TypeManager` configure method.
|
|
727
|
+
|
|
728
|
+
### Deserializable decorator
|
|
729
|
+
|
|
730
|
+
This decorator is used to enable or disable deserialization for a certain property.
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
import { Type, Property, Deserializable } from '@dipscope/type-manager';
|
|
734
|
+
|
|
735
|
+
@Type()
|
|
736
|
+
export class User
|
|
737
|
+
{
|
|
738
|
+
@Property(String) @Deserializable() public name: string;
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
Such declaration is an alternative for:
|
|
743
|
+
|
|
744
|
+
```typescript
|
|
745
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
746
|
+
|
|
747
|
+
@Type()
|
|
748
|
+
export class User
|
|
749
|
+
{
|
|
750
|
+
@Property(String, { deserializable: true }) public name: string;
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
By default all properties are deserializable.
|
|
755
|
+
|
|
756
|
+
### Discriminant decorator
|
|
757
|
+
|
|
758
|
+
This decorator is used to define a custom discriminant for a type which is later used during serialization and deserialization of polymorphic types.
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
import { Type, Property, Discriminant } from '@dipscope/type-manager';
|
|
762
|
+
|
|
763
|
+
@Type()
|
|
764
|
+
@Discriminant('Company.Api.Entities.User')
|
|
598
765
|
export class User
|
|
599
766
|
{
|
|
600
767
|
@Property(String) public name: string;
|
|
@@ -607,7 +774,7 @@ Such declaration is an alternative for:
|
|
|
607
774
|
import { Type, Property } from '@dipscope/type-manager';
|
|
608
775
|
|
|
609
776
|
@Type({
|
|
610
|
-
|
|
777
|
+
discriminant: 'Company.Api.Entities.User'
|
|
611
778
|
})
|
|
612
779
|
export class User
|
|
613
780
|
{
|
|
@@ -615,20 +782,20 @@ export class User
|
|
|
615
782
|
}
|
|
616
783
|
```
|
|
617
784
|
|
|
618
|
-
|
|
785
|
+
You can read more about handling of polymorphic types in this [section](#configuring-usage-of-polymorphic-types).
|
|
619
786
|
|
|
620
|
-
###
|
|
787
|
+
### Discriminator decorator
|
|
621
788
|
|
|
622
|
-
|
|
789
|
+
This decorator can be used to define a custom property which stores discriminant of polymorphic type.
|
|
623
790
|
|
|
624
791
|
```typescript
|
|
625
|
-
import { Type, Property,
|
|
792
|
+
import { Type, Property, Discriminator } from '@dipscope/type-manager';
|
|
626
793
|
|
|
627
794
|
@Type()
|
|
628
|
-
@
|
|
795
|
+
@Discriminator('__typename__')
|
|
629
796
|
export class User
|
|
630
797
|
{
|
|
631
|
-
@Property(String)
|
|
798
|
+
@Property(String) public name: string;
|
|
632
799
|
}
|
|
633
800
|
```
|
|
634
801
|
|
|
@@ -638,25 +805,25 @@ Such declaration is an alternative for:
|
|
|
638
805
|
import { Type, Property } from '@dipscope/type-manager';
|
|
639
806
|
|
|
640
807
|
@Type({
|
|
641
|
-
|
|
808
|
+
discriminator: '__typename__'
|
|
642
809
|
})
|
|
643
810
|
export class User
|
|
644
811
|
{
|
|
645
|
-
@Property(String
|
|
812
|
+
@Property(String) public name: string;
|
|
646
813
|
}
|
|
647
814
|
```
|
|
648
815
|
|
|
649
|
-
|
|
816
|
+
In common use cases discriminator should be set globally using `TypeManager` configure method. Using this option on a type level makes sense only if discriminator differs from the global one. You can read more about handling of polymorphic types in this [section](#configuring-usage-of-polymorphic-types).
|
|
650
817
|
|
|
651
|
-
###
|
|
818
|
+
### Factory decorator
|
|
652
819
|
|
|
653
|
-
|
|
820
|
+
This decorator can be used to register a handler which should be used for constructing a type instead of default one.
|
|
654
821
|
|
|
655
822
|
```typescript
|
|
656
|
-
import { Type, Property,
|
|
823
|
+
import { Type, Property, Factory } from '@dipscope/type-manager';
|
|
657
824
|
|
|
658
825
|
@Type()
|
|
659
|
-
@
|
|
826
|
+
@Factory(new UserFactory())
|
|
660
827
|
export class User
|
|
661
828
|
{
|
|
662
829
|
@Property(String) public name: string;
|
|
@@ -669,7 +836,7 @@ Such declaration is an alternative for:
|
|
|
669
836
|
import { Type, Property } from '@dipscope/type-manager';
|
|
670
837
|
|
|
671
838
|
@Type({
|
|
672
|
-
|
|
839
|
+
factory: new UserFactory()
|
|
673
840
|
})
|
|
674
841
|
export class User
|
|
675
842
|
{
|
|
@@ -677,19 +844,20 @@ export class User
|
|
|
677
844
|
}
|
|
678
845
|
```
|
|
679
846
|
|
|
680
|
-
|
|
847
|
+
This may be useful in cases when you want to init some special application specific properties. Read more about [defining custom factory](#defining-custom-factory) in a separate section.
|
|
681
848
|
|
|
682
849
|
### Injectable decorator
|
|
683
850
|
|
|
684
|
-
|
|
851
|
+
This decorator is used to register a type in dependency injection container.
|
|
685
852
|
|
|
686
853
|
```typescript
|
|
687
854
|
import { Injectable } from '@dipscope/type-manager';
|
|
688
855
|
|
|
856
|
+
@Type()
|
|
689
857
|
@Injectable()
|
|
690
858
|
export class UserService
|
|
691
859
|
{
|
|
692
|
-
public
|
|
860
|
+
public property: string;
|
|
693
861
|
}
|
|
694
862
|
```
|
|
695
863
|
|
|
@@ -703,11 +871,11 @@ import { Type } from '@dipscope/type-manager';
|
|
|
703
871
|
})
|
|
704
872
|
export class UserService
|
|
705
873
|
{
|
|
706
|
-
public
|
|
874
|
+
public property: string;
|
|
707
875
|
}
|
|
708
876
|
```
|
|
709
877
|
|
|
710
|
-
|
|
878
|
+
Injectable type later can be provided as a dependency.
|
|
711
879
|
|
|
712
880
|
```typescript
|
|
713
881
|
import { Type, Property, Inject } from '@dipscope/type-manager';
|
|
@@ -726,20 +894,147 @@ export class User
|
|
|
726
894
|
}
|
|
727
895
|
```
|
|
728
896
|
|
|
729
|
-
In most cases you will work in environment where dependency injection system is already setted up. In this case you have to implement custom `Injector` to be used instead of our default one. Besides you should follow the steps to register injectable services specified by the vendor. This means that you should not use `Injectable` decorator from our package. You can read more about [creating a custom injector](#defining-custom-injector) in
|
|
897
|
+
In most cases you will work in environment where dependency injection system is already setted up. In this case you have to implement custom `Injector` to be used instead of our default one. Besides you should follow the steps to register injectable services specified by the vendor. This means that you should not use `Injectable` decorator from our package. You can read more about [creating a custom injector](#defining-custom-injector) in a separate section.
|
|
898
|
+
|
|
899
|
+
### Injector decorator
|
|
900
|
+
|
|
901
|
+
This decorator can be used to define a custom injector implementation which should be used in a type scope.
|
|
902
|
+
|
|
903
|
+
```typescript
|
|
904
|
+
import { Type, Property, Injector } from '@dipscope/type-manager';
|
|
905
|
+
|
|
906
|
+
@Type()
|
|
907
|
+
@Injector(new UserInjector())
|
|
908
|
+
export class User
|
|
909
|
+
{
|
|
910
|
+
@Property(String) public name: string;
|
|
911
|
+
}
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
Such declaration is an alternative for:
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
918
|
+
|
|
919
|
+
@Type({
|
|
920
|
+
injector: new UserInjector()
|
|
921
|
+
})
|
|
922
|
+
export class User
|
|
923
|
+
{
|
|
924
|
+
@Property(String) public name: string;
|
|
925
|
+
}
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
In most cases this is not required and the common use case is to specify injector globally instead. You can read more about [defining custom injector](#defining-custom-injector) in a separate section.
|
|
730
929
|
|
|
731
|
-
###
|
|
930
|
+
### Naming convention decorator
|
|
732
931
|
|
|
733
|
-
This
|
|
932
|
+
This decorator can be used both on type and property to provide custom naming strategy.
|
|
734
933
|
|
|
735
934
|
```typescript
|
|
736
|
-
import { Type, Property,
|
|
935
|
+
import { Type, Property, NamingConvention } from '@dipscope/type-manager';
|
|
936
|
+
import { CamelCaseNamingConvention, SnakeCaseNamingConvention } from '@dipscope/type-manager/naming-conventions';
|
|
937
|
+
|
|
938
|
+
@Type()
|
|
939
|
+
@NamingConvention(new CamelCaseNamingConvention())
|
|
940
|
+
export class User
|
|
941
|
+
{
|
|
942
|
+
@Property(String) @NamingConvention(new SnakeCaseNamingConvention()) public name: string;
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
Such declaration is an alternative for:
|
|
947
|
+
|
|
948
|
+
```typescript
|
|
949
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
950
|
+
import { CamelCaseNamingConvention, SnakeCaseNamingConvention } from '@dipscope/type-manager/naming-conventions';
|
|
951
|
+
|
|
952
|
+
@Type({
|
|
953
|
+
namingConvention: new CamelCaseNamingConvention()
|
|
954
|
+
})
|
|
955
|
+
export class User
|
|
956
|
+
{
|
|
957
|
+
@Property(String, { namingConvention: new SnakeCaseNamingConvention() }) public name: string;
|
|
958
|
+
}
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
In most cases this is not required and the common use case is to specify naming strategy globally instead. You can read more about [configuring naming convention](#configuring-naming-convention) in a separate section.
|
|
962
|
+
|
|
963
|
+
### Preserve discriminator decorator
|
|
964
|
+
|
|
965
|
+
This decorator defines if discriminator should be preserved in objects during serialization and deserialization.
|
|
966
|
+
|
|
967
|
+
```typescript
|
|
968
|
+
import { Type, Property, PreserveDiscriminator } from '@dipscope/type-manager';
|
|
969
|
+
|
|
970
|
+
@Type()
|
|
971
|
+
@PreserveDiscriminator()
|
|
972
|
+
export class User
|
|
973
|
+
{
|
|
974
|
+
@Property(String) public name: string;
|
|
975
|
+
}
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
Such declaration is an alternative for:
|
|
979
|
+
|
|
980
|
+
```typescript
|
|
981
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
982
|
+
|
|
983
|
+
@Type({
|
|
984
|
+
preserveDiscriminator: true
|
|
985
|
+
})
|
|
986
|
+
export class User
|
|
987
|
+
{
|
|
988
|
+
@Property(String) public name: string;
|
|
989
|
+
}
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
By default discriminator is not preserved and only used during deserialization of polymorphic types. You can read more about handling of polymorphic types in this [section](#configuring-usage-of-polymorphic-types).
|
|
993
|
+
|
|
994
|
+
### Reference handler decorator
|
|
995
|
+
|
|
996
|
+
This decorator can be used both on type and property to specify how references to the same objects should be handled during serialization and deserialization.
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
import { Type, Property, ReferenceHandler } from '@dipscope/type-manager';
|
|
1000
|
+
import { DirectReferenceHandler, LeadReferenceHandler } from '@dipscope/type-manager/reference-handlers';
|
|
1001
|
+
|
|
1002
|
+
@Type()
|
|
1003
|
+
@ReferenceHandler(new DirectReferenceHandler())
|
|
1004
|
+
export class User
|
|
1005
|
+
{
|
|
1006
|
+
@Property(String) @ReferenceHandler(new LeadReferenceHandler()) public name: string;
|
|
1007
|
+
}
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
Such declaration is an alternative for:
|
|
1011
|
+
|
|
1012
|
+
```typescript
|
|
1013
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
1014
|
+
import { DirectReferenceHandler, LeadReferenceHandler } from '@dipscope/type-manager/reference-handlers';
|
|
1015
|
+
|
|
1016
|
+
@Type({
|
|
1017
|
+
referenceHandler: new DirectReferenceHandler()
|
|
1018
|
+
})
|
|
1019
|
+
export class User
|
|
1020
|
+
{
|
|
1021
|
+
@Property(String, { referenceHandler: new LeadReferenceHandler() }) public name: string;
|
|
1022
|
+
}
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
In most cases this is not required and the common use case is to specify reference handler globally instead. You can read more about [configuring reference handler](#configuring-reference-handler) in a separate section.
|
|
1026
|
+
|
|
1027
|
+
### Serializable decorator
|
|
1028
|
+
|
|
1029
|
+
This decorator is used to enable or disable serialization for a certain property.
|
|
1030
|
+
|
|
1031
|
+
```typescript
|
|
1032
|
+
import { Type, Property, Serializable } from '@dipscope/type-manager';
|
|
737
1033
|
|
|
738
1034
|
@Type()
|
|
739
1035
|
export class User
|
|
740
1036
|
{
|
|
741
1037
|
@Property(String) @Serializable() public name: string;
|
|
742
|
-
@Property(String) @Deserializable() public email: string;
|
|
743
1038
|
}
|
|
744
1039
|
```
|
|
745
1040
|
|
|
@@ -752,24 +1047,23 @@ import { Type, Property } from '@dipscope/type-manager';
|
|
|
752
1047
|
export class User
|
|
753
1048
|
{
|
|
754
1049
|
@Property(String, { serializable: true }) public name: string;
|
|
755
|
-
@Property(String, { deserializable: true }) public email: string;
|
|
756
1050
|
}
|
|
757
1051
|
```
|
|
758
1052
|
|
|
759
|
-
By default all properties are serializable
|
|
1053
|
+
By default all properties are serializable.
|
|
760
1054
|
|
|
761
|
-
###
|
|
1055
|
+
### Serializer decorator
|
|
762
1056
|
|
|
763
|
-
This decorator is used to define
|
|
1057
|
+
This decorator is used to define custom serializer for a type or property.
|
|
764
1058
|
|
|
765
1059
|
```typescript
|
|
766
|
-
import { Type, Property,
|
|
1060
|
+
import { Type, Property, Serializer } from '@dipscope/type-manager';
|
|
767
1061
|
|
|
768
1062
|
@Type()
|
|
769
|
-
@
|
|
1063
|
+
@Serializer(new UserSerializer())
|
|
770
1064
|
export class User
|
|
771
1065
|
{
|
|
772
|
-
@Property(String) @
|
|
1066
|
+
@Property(String) @Serializer(new UserNameSerializer()) public name: string;
|
|
773
1067
|
}
|
|
774
1068
|
```
|
|
775
1069
|
|
|
@@ -779,15 +1073,15 @@ Such declaration is an alternative for:
|
|
|
779
1073
|
import { Type, Property } from '@dipscope/type-manager';
|
|
780
1074
|
|
|
781
1075
|
@Type({
|
|
782
|
-
|
|
1076
|
+
serializer: new UserSerializer()
|
|
783
1077
|
})
|
|
784
1078
|
export class User
|
|
785
1079
|
{
|
|
786
|
-
@Property(String, {
|
|
1080
|
+
@Property(String, { serializer: new UserNameSerializer() }) public name: string;
|
|
787
1081
|
}
|
|
788
1082
|
```
|
|
789
1083
|
|
|
790
|
-
|
|
1084
|
+
Custom serializer should be an implementation of `Serializer` interface. You can read more about [creating a custom serializer](#defining-custom-serializer) in a separate section.
|
|
791
1085
|
|
|
792
1086
|
### Use default value decorator
|
|
793
1087
|
|
|
@@ -822,7 +1116,7 @@ Using default values is turned off by default. You can enable them globally usin
|
|
|
822
1116
|
|
|
823
1117
|
### Use implicit conversion decorator
|
|
824
1118
|
|
|
825
|
-
By default if declared type will not match provided during serialization or deserialization
|
|
1119
|
+
By default if declared type will not match provided during serialization or deserialization an error will be logged and result value will be undefined. This means that for example assigning `Number` to `String` will not work as `StringSerializer` expects `String`. However `Number` and other types can be converted to `String` for you when implicit conversion is enabled.
|
|
826
1120
|
|
|
827
1121
|
```typescript
|
|
828
1122
|
import { Type, Property, UseImplicitConversion } from '@dipscope/type-manager';
|
|
@@ -846,11 +1140,11 @@ export class User
|
|
|
846
1140
|
}
|
|
847
1141
|
```
|
|
848
1142
|
|
|
849
|
-
With this any value which can be converted to `
|
|
1143
|
+
With this any value which can be converted to `String` will be converted properly. Such behaviour works for other built in serializers and supported for custom ones. By default implicit conversion is turned off. You can enable it using `UseImplicitConversion` decorator per type and property or enable globally using `TypeManager` configure method.
|
|
850
1144
|
|
|
851
1145
|
## Defining configuration manually
|
|
852
1146
|
|
|
853
|
-
There are circumstances when decorators cannot be used or you don't want to. For example you are using a 3rd party package and cannot decorate classes from it. Another use case
|
|
1147
|
+
There are circumstances when decorators cannot be used or you don't want to. For example you are using a 3rd party package and cannot decorate classes from it. Another use case when you want to configure some options globally. In such scenarios you can define the complete configuration through special static configure methods.
|
|
854
1148
|
|
|
855
1149
|
We have separate methods to configure each type manager option, so the provided examples can be simplified to avoid creating additional object. It is useful when you need to configure only one option. In our examples we are always use the main one to give you a general overview.
|
|
856
1150
|
|
|
@@ -862,7 +1156,7 @@ There are several options which can be configured globally. For example let's ov
|
|
|
862
1156
|
import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
863
1157
|
import { TypeOptionsBase } from '@dipscope/type-manager/core';
|
|
864
1158
|
|
|
865
|
-
const typeOptionsBase: TypeOptionsBase = {
|
|
1159
|
+
const typeOptionsBase: TypeOptionsBase<any> = {
|
|
866
1160
|
useDefaultValue: true
|
|
867
1161
|
};
|
|
868
1162
|
|
|
@@ -880,8 +1174,9 @@ For the full list of available global options check `TypeOptionsBase` definition
|
|
|
880
1174
|
Here is an example of declarative configuration which can be used for 3rd party or your own classes in addition to decorators.
|
|
881
1175
|
|
|
882
1176
|
```typescript
|
|
1177
|
+
import { DateTime } from '@external-library';
|
|
883
1178
|
import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
884
|
-
import { TypeOptions,
|
|
1179
|
+
import { TypeFn, TypeOptions, PropertyName, PropertyOptions } from '@dipscope/type-manager/core';
|
|
885
1180
|
|
|
886
1181
|
const dateTimeOptions: TypeOptions<DateTime> = {
|
|
887
1182
|
alias: 'DateTime',
|
|
@@ -890,14 +1185,14 @@ const dateTimeOptions: TypeOptions<DateTime> = {
|
|
|
890
1185
|
|
|
891
1186
|
const userOptions: TypeOptions<User> = {
|
|
892
1187
|
alias: 'User',
|
|
893
|
-
propertyOptionsMap: new Map<
|
|
1188
|
+
propertyOptionsMap: new Map<PropertyName, PropertyOptions<any>>(
|
|
894
1189
|
['name', { typeArgument: String, serializable: true, alias: 'username' }],
|
|
895
1190
|
['createdAt', { typeArgument: DateTime }]
|
|
896
1191
|
)
|
|
897
1192
|
};
|
|
898
1193
|
|
|
899
1194
|
const typeManagerOptions: TypeManagerOptions = {
|
|
900
|
-
typeOptionsMap: new Map<
|
|
1195
|
+
typeOptionsMap: new Map<TypeFn<any>, TypeOptions<any>>(
|
|
901
1196
|
[DateTime, dateTimeOptions],
|
|
902
1197
|
[User, userOptions]
|
|
903
1198
|
)
|
|
@@ -950,6 +1245,239 @@ export class User
|
|
|
950
1245
|
}
|
|
951
1246
|
```
|
|
952
1247
|
|
|
1248
|
+
### Configuring usage of polymorphic types
|
|
1249
|
+
|
|
1250
|
+
Let's assume we are working with a shapes. To describe different types of shape we have to create an abstract class with several descendants.
|
|
1251
|
+
|
|
1252
|
+
```typescript
|
|
1253
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
1254
|
+
|
|
1255
|
+
@Type()
|
|
1256
|
+
export abstract class Shape
|
|
1257
|
+
{
|
|
1258
|
+
@Property(String) public title: string;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
@Type()
|
|
1262
|
+
export class Rectangle extends Shape
|
|
1263
|
+
{
|
|
1264
|
+
@Property(Number) public width: number;
|
|
1265
|
+
@Property(Number) public height: number;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
@Type()
|
|
1269
|
+
export class Square extends Shape
|
|
1270
|
+
{
|
|
1271
|
+
@Property(Number) public width: number;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
@Type()
|
|
1275
|
+
export class Circle extends Shape
|
|
1276
|
+
{
|
|
1277
|
+
@Property(Number) public radius: number;
|
|
1278
|
+
}
|
|
1279
|
+
```
|
|
1280
|
+
|
|
1281
|
+
Some other class declares a `shapes` property in it.
|
|
1282
|
+
|
|
1283
|
+
```typescript
|
|
1284
|
+
import { Type, Property } from '@dipscope/type-manager';
|
|
1285
|
+
|
|
1286
|
+
@Type()
|
|
1287
|
+
export class Plot
|
|
1288
|
+
{
|
|
1289
|
+
@Property(Array, [Shape]) public shapes: Shape[];
|
|
1290
|
+
}
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
From the perspective of declaration everything looks ok but from the point of serialization some things may become complicated. Shapes property can store `Rectangle`, `Square` or `Circle`. Each of this classes have different properties. Here is an example of JSON.
|
|
1294
|
+
|
|
1295
|
+
```json
|
|
1296
|
+
{
|
|
1297
|
+
"shapes": [
|
|
1298
|
+
{
|
|
1299
|
+
"title": "Cool rectangle",
|
|
1300
|
+
"width": 10,
|
|
1301
|
+
"height": 10
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
"title": "Perfect square",
|
|
1305
|
+
"width": 10
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
"title": "Simple circle",
|
|
1309
|
+
"radius": 6
|
|
1310
|
+
}
|
|
1311
|
+
]
|
|
1312
|
+
}
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
During deserialization of this JSON to a `Plot` class we only aware that all plain objects inside an `Array` are somehow related to a `Shape` type. So any options to handle this?
|
|
1316
|
+
|
|
1317
|
+
Luckily we have a `TypeManager`. When you declaring you types using decorators or declarative style it builds inheritance graph between them which can be used during serialization and deserialization.
|
|
1318
|
+
|
|
1319
|
+
It uses 2 special configurable type options:
|
|
1320
|
+
|
|
1321
|
+
* `Discriminator` which defines a property inside an object which should be used to define a type.
|
|
1322
|
+
* `Discriminant` which represents a certain `Discriminator` value.
|
|
1323
|
+
|
|
1324
|
+
This options have default values if you have not configured them explicitly.
|
|
1325
|
+
|
|
1326
|
+
* Default value of discriminator is a `$type`. During deserialization `TypeManager` expects such property to be present inside a polymorphic object.
|
|
1327
|
+
* Default value of discriminant is a `ClassName` which determined based on the type function.
|
|
1328
|
+
|
|
1329
|
+
For proper deserialization of polymorphic types you have to provide such information inside your JSON.
|
|
1330
|
+
|
|
1331
|
+
```json
|
|
1332
|
+
{
|
|
1333
|
+
"shapes": [
|
|
1334
|
+
{
|
|
1335
|
+
"$type": "Rectangle",
|
|
1336
|
+
"title": "Cool rectangle",
|
|
1337
|
+
"width": 10,
|
|
1338
|
+
"height": 10
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
"$type": "Square",
|
|
1342
|
+
"title": "Perfect square",
|
|
1343
|
+
"width": 10
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
"$type": "Circle",
|
|
1347
|
+
"title": "Simple circle",
|
|
1348
|
+
"radius": 6
|
|
1349
|
+
}
|
|
1350
|
+
]
|
|
1351
|
+
}
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
Now your JSON will be handled properly and you will get `Rectangle`, `Square` and `Circle` class instances in return.
|
|
1355
|
+
|
|
1356
|
+
In some cases your `Discriminator` or `Discriminant` values will not match to our default ones. For example library like [Json.NET](https://www.newtonsoft.com/json) can be used on the backend side to send a response from your API. It uses `$type` property as `Discriminator` and full name of class as `Discriminant`. In such scenario our JSON may look like this.
|
|
1357
|
+
|
|
1358
|
+
```json
|
|
1359
|
+
{
|
|
1360
|
+
"shapes": [
|
|
1361
|
+
{
|
|
1362
|
+
"$type": "Company.Api.Entities.Rectangle",
|
|
1363
|
+
"title": "Cool rectangle",
|
|
1364
|
+
"width": 10,
|
|
1365
|
+
"height": 10
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
"$type": "Company.Api.Entities.Square",
|
|
1369
|
+
"title": "Perfect square",
|
|
1370
|
+
"width": 10
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
"$type": "Company.Api.Entities.Circle",
|
|
1374
|
+
"title": "Simple circle",
|
|
1375
|
+
"radius": 6
|
|
1376
|
+
}
|
|
1377
|
+
]
|
|
1378
|
+
}
|
|
1379
|
+
```
|
|
1380
|
+
|
|
1381
|
+
To change `Discriminator` globally you have to use `TypeManager` configure method.
|
|
1382
|
+
|
|
1383
|
+
```typescript
|
|
1384
|
+
import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
1385
|
+
import { TypeOptionsBase } from '@dipscope/type-manager/core';
|
|
1386
|
+
|
|
1387
|
+
const typeOptionsBase: TypeOptionsBase<any> = {
|
|
1388
|
+
discriminator: '$customType'
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
const typeManagerOptions: TypeManagerOptions = {
|
|
1392
|
+
typeOptionsBase: typeOptionsBase
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1395
|
+
TypeManager.configure(typeManagerOptions);
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
To change `Discriminant` you have to use per type configuration.
|
|
1399
|
+
|
|
1400
|
+
```typescript
|
|
1401
|
+
import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
1402
|
+
import { TypeFn, TypeOptions, PropertyName, PropertyOptions } from '@dipscope/type-manager/core';
|
|
1403
|
+
|
|
1404
|
+
const rectangleOptions: TypeOptions<Rectangle> = {
|
|
1405
|
+
discriminant: 'Company.Api.Entities.Rectangle'
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
const squareOptions: TypeOptions<Square> = {
|
|
1409
|
+
discriminant: 'Company.Api.Entities.Square'
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
const circleOptions: TypeOptions<Circle> = {
|
|
1413
|
+
discriminant: 'Company.Api.Entities.Circle'
|
|
1414
|
+
};
|
|
1415
|
+
|
|
1416
|
+
const typeManagerOptions: TypeManagerOptions = {
|
|
1417
|
+
typeOptionsMap: new Map<TypeFn<any>, TypeOptions<any>>(
|
|
1418
|
+
[Rectangle, rectangleOptions],
|
|
1419
|
+
[Square, squareOptions],
|
|
1420
|
+
[Circle, circleOptions]
|
|
1421
|
+
)
|
|
1422
|
+
};
|
|
1423
|
+
|
|
1424
|
+
TypeManager.configure(typeManagerOptions);
|
|
1425
|
+
```
|
|
1426
|
+
|
|
1427
|
+
As an alternative you can change `Discriminant` as the following using decorators.
|
|
1428
|
+
|
|
1429
|
+
```typescript
|
|
1430
|
+
import { Type, Property, Discriminant } from '@dipscope/type-manager';
|
|
1431
|
+
|
|
1432
|
+
@Type()
|
|
1433
|
+
@Discriminant('Company.Api.Entities.Shape')
|
|
1434
|
+
export abstract class Shape
|
|
1435
|
+
{
|
|
1436
|
+
@Property(String) public title: string;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
@Type()
|
|
1440
|
+
@Discriminant('Company.Api.Entities.Rectangle')
|
|
1441
|
+
export class Rectangle extends Shape
|
|
1442
|
+
{
|
|
1443
|
+
@Property(Number) public width: number;
|
|
1444
|
+
@Property(Number) public height: number;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
@Type()
|
|
1448
|
+
@Discriminant('Company.Api.Entities.Square')
|
|
1449
|
+
export class Square extends Shape
|
|
1450
|
+
{
|
|
1451
|
+
@Property(Number) public width: number;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
@Type()
|
|
1455
|
+
@Discriminant('Company.Api.Entities.Circle')
|
|
1456
|
+
export class Circle extends Shape
|
|
1457
|
+
{
|
|
1458
|
+
@Property(Number) public radius: number;
|
|
1459
|
+
}
|
|
1460
|
+
```
|
|
1461
|
+
|
|
1462
|
+
By default `Discriminator` is not preserved inside objects and only used during deserialization. You can change this behavior by enabling preserving of discriminator globally or per type.
|
|
1463
|
+
|
|
1464
|
+
```typescript
|
|
1465
|
+
import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
1466
|
+
import { TypeOptionsBase } from '@dipscope/type-manager/core';
|
|
1467
|
+
|
|
1468
|
+
const typeOptionsBase: TypeOptionsBase<any> = {
|
|
1469
|
+
preserveDiscriminator: true
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
const typeManagerOptions: TypeManagerOptions = {
|
|
1473
|
+
typeOptionsBase: typeOptionsBase
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
TypeManager.configure(typeManagerOptions);
|
|
1477
|
+
```
|
|
1478
|
+
|
|
1479
|
+
With this option enabled discriminator will be present in output data.
|
|
1480
|
+
|
|
953
1481
|
### Configuring naming convention
|
|
954
1482
|
|
|
955
1483
|
Naming convention specifies how each declared property of a type should be treated when reading it from JSON. By default names are read as is. Let's assume we have a `User` class in camel case naming convention for properties.
|
|
@@ -1042,7 +1570,7 @@ import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
|
1042
1570
|
import { TypeOptionsBase } from '@dipscope/type-manager/core';
|
|
1043
1571
|
import { SnakeCaseNamingConvention } from '@dipscope/type-manager/naming-conventions';
|
|
1044
1572
|
|
|
1045
|
-
const typeOptionsBase: TypeOptionsBase = {
|
|
1573
|
+
const typeOptionsBase: TypeOptionsBase<any> = {
|
|
1046
1574
|
namingConvention: new SnakeCaseNamingConvention()
|
|
1047
1575
|
};
|
|
1048
1576
|
|
|
@@ -1053,7 +1581,7 @@ const typeManagerOptions: TypeManagerOptions = {
|
|
|
1053
1581
|
TypeManager.configure(typeManagerOptions);
|
|
1054
1582
|
```
|
|
1055
1583
|
|
|
1056
|
-
Now all property names will be converted to snake case while reading them from JSON. If you have not found suitable naming convention you can easily implement your own. Read more about [creating a custom naming convention](#defining-custom-naming-convention) in
|
|
1584
|
+
Now all property names will be converted to snake case while reading them from JSON. If you have not found suitable naming convention you can easily implement your own. Read more about [creating a custom naming convention](#defining-custom-naming-convention) in a separate section.
|
|
1057
1585
|
|
|
1058
1586
|
### Configuring reference handler
|
|
1059
1587
|
|
|
@@ -1086,14 +1614,13 @@ Somewhere in code you have such a logic:
|
|
|
1086
1614
|
```typescript
|
|
1087
1615
|
import { TypeManager } from '@dipscope/type-manager';
|
|
1088
1616
|
|
|
1089
|
-
const
|
|
1090
|
-
const
|
|
1091
|
-
const company = new Company();
|
|
1617
|
+
const user = new User();
|
|
1618
|
+
const company = new Company();
|
|
1092
1619
|
|
|
1093
1620
|
user.company = company;
|
|
1094
1621
|
company.user = user;
|
|
1095
1622
|
|
|
1096
|
-
const result =
|
|
1623
|
+
const result = TypeManager.serialize(User, user);
|
|
1097
1624
|
```
|
|
1098
1625
|
|
|
1099
1626
|
Here are results returned by different reference handlers:
|
|
@@ -1109,14 +1636,14 @@ Here are results returned by different reference handlers:
|
|
|
1109
1636
|
{ company: { user: undefined };
|
|
1110
1637
|
```
|
|
1111
1638
|
|
|
1112
|
-
As you can see `DirectReferenceHandler` does not make changes to your data and completely fine until you have to convert circular reference structure to a string. `JSON.stringify` method which we are using under the hood does not support such conversions
|
|
1639
|
+
As you can see `DirectReferenceHandler` does not make changes to your data and completely fine until you have to convert circular reference structure to a string. `JSON.stringify` method which we are using under the hood does not support such conversions so you will encounter an error. In this case you can select another reference handler. For example `PathReferenceHandler` which produces JSON string using JSONPath format for circular references supported by many libraries. Or you can simply ignore circular reference when it should be converted to a string and use `LeadReferenceHandler`. To change default reference handler you have to use `TypeManager` configure methods.
|
|
1113
1640
|
|
|
1114
1641
|
```typescript
|
|
1115
1642
|
import { TypeManagerOptions } from '@dipscope/type-manager';
|
|
1116
1643
|
import { TypeOptionsBase } from '@dipscope/type-manager/core';
|
|
1117
1644
|
import { PathReferenceHandler } from '@dipscope/type-manager/reference-handlers';
|
|
1118
1645
|
|
|
1119
|
-
const typeOptionsBase: TypeOptionsBase = {
|
|
1646
|
+
const typeOptionsBase: TypeOptionsBase<any> = {
|
|
1120
1647
|
referenceHandler: new PathReferenceHandler()
|
|
1121
1648
|
};
|
|
1122
1649
|
|
|
@@ -1127,7 +1654,7 @@ const typeManagerOptions: TypeManagerOptions = {
|
|
|
1127
1654
|
TypeManager.configure(typeManagerOptions);
|
|
1128
1655
|
```
|
|
1129
1656
|
|
|
1130
|
-
With such configuration any
|
|
1657
|
+
With such configuration any reference will be handled using JSONPath so you are completely free from errors during conversion to a string.
|
|
1131
1658
|
|
|
1132
1659
|
## Advanced usage
|
|
1133
1660
|
|
|
@@ -1148,14 +1675,13 @@ class User
|
|
|
1148
1675
|
}
|
|
1149
1676
|
```
|
|
1150
1677
|
|
|
1151
|
-
This allows you to get it later in serializers, factories or
|
|
1678
|
+
This allows you to get it later in serializers, factories, injectors or your code and perform specific actions. Besides pipeline you can get this data in any place you want using `TypeManager`.
|
|
1152
1679
|
|
|
1153
1680
|
```typescript
|
|
1154
1681
|
import { TypeManager } from '@dipscope/type-manager';
|
|
1155
1682
|
|
|
1156
|
-
const
|
|
1157
|
-
const
|
|
1158
|
-
const customData = userMetadata.customData;
|
|
1683
|
+
const userMetadata = TypeManager.extractTypeMetadata(User);
|
|
1684
|
+
const customData = userMetadata.customData;
|
|
1159
1685
|
|
|
1160
1686
|
// Do something with type custom data...
|
|
1161
1687
|
|
|
@@ -1169,7 +1695,7 @@ for (const propertyMetadata of userMetadata.propertyMetadataMap.values())
|
|
|
1169
1695
|
|
|
1170
1696
|
### Defining custom serializer
|
|
1171
1697
|
|
|
1172
|
-
You can create your own serializer or replace built in one. First you have to implement `Serializer` interface. It declares `serialize` and `deserialize` methods. Serialize method is called during conversion of
|
|
1698
|
+
You can create your own serializer or replace built in one. First you have to implement `Serializer` interface. It declares `serialize` and `deserialize` methods. Serialize method is called during conversion of `JavaScript` object instance into a plain object. Deserialize method is called during backward conversion. Here is an example of possible definition for custom `DateTime` class.
|
|
1173
1699
|
|
|
1174
1700
|
```typescript
|
|
1175
1701
|
import { Serializer, TypeLike, SerializerContext, Fn } from '@dipscope/type-manager/core';
|
|
@@ -1230,7 +1756,7 @@ export class DateTimeSerializer implements Serializer<DateTime>
|
|
|
1230
1756
|
|
|
1231
1757
|
This example follows internal conventions and gives you a picture of how real serializer may look like. `TypeManager` does not perform any checks and just passes values directly to serializer. Thats why input values can be undefined, null or others depending from what is stored inside certain property. `TypeLike` is an internal type to declare this behaviour.
|
|
1232
1758
|
|
|
1233
|
-
Serializer implementation is fully responsible for return result. You can get default values, custom data and other options specified in
|
|
1759
|
+
Serializer implementation is fully responsible for return result. You can get default values, custom data and other options specified in configuration from current serializer context and react accordingly. For example you can check if implicit conversion is enabled and convert any value to the target one.
|
|
1234
1760
|
|
|
1235
1761
|
When you are finished with definitions there are two possible ways to register a serializer. You can use decorators.
|
|
1236
1762
|
|
|
@@ -1279,7 +1805,7 @@ export class CustomInjector implements Injector
|
|
|
1279
1805
|
|
|
1280
1806
|
public get<TType>(typeMetadata: TypeMetadata<TType>): TType | undefined
|
|
1281
1807
|
{
|
|
1282
|
-
return this.angularInjector.get(typeMetadata.
|
|
1808
|
+
return this.angularInjector.get(typeMetadata.typeFn);
|
|
1283
1809
|
}
|
|
1284
1810
|
}
|
|
1285
1811
|
```
|
|
@@ -1316,7 +1842,7 @@ export class CustomTypeFactory extends TypeFactory
|
|
|
1316
1842
|
|
|
1317
1843
|
// Resolve custom data.
|
|
1318
1844
|
const typeMetadata = typeContext.typeMetadata;
|
|
1319
|
-
const customData
|
|
1845
|
+
const customData = typeMetadata.customData;
|
|
1320
1846
|
|
|
1321
1847
|
// Process custom data.
|
|
1322
1848
|
for (const propertyName in customData)
|
|
@@ -1407,7 +1933,7 @@ Now property names will be resolved using your custom naming convention.
|
|
|
1407
1933
|
|
|
1408
1934
|
## Use cases
|
|
1409
1935
|
|
|
1410
|
-
This section describes
|
|
1936
|
+
This section describes certain use cases to separate them from the main documentation part. We will point to a concrete place you should read to setup what is necessary.
|
|
1411
1937
|
|
|
1412
1938
|
### Built in serializers
|
|
1413
1939
|
|
|
@@ -1434,9 +1960,9 @@ Here is a list of types with built in serializers.
|
|
|
1434
1960
|
|
|
1435
1961
|
For these you don't have to do anything to make them work. You are free to [create a custom serializer](#defining-custom-serializer) or override built in one if it does not cover your use case.
|
|
1436
1962
|
|
|
1437
|
-
###
|
|
1963
|
+
### Circular object references
|
|
1438
1964
|
|
|
1439
|
-
|
|
1965
|
+
We have a great support for circular references with different behaviour when they are detected. Read [configuring reference handler](#configuring-reference-handler) section for more info.
|
|
1440
1966
|
|
|
1441
1967
|
### Dependency injection and immutable types
|
|
1442
1968
|
|
|
@@ -1444,11 +1970,15 @@ Follow the steps described in [inject decorator](#inject-decorator) section. To
|
|
|
1444
1970
|
|
|
1445
1971
|
### Different case usage in class and JSON
|
|
1446
1972
|
|
|
1447
|
-
If your cases differs between class and JSON. For example camelCase vs snake_case
|
|
1973
|
+
If your cases differs between class and JSON. For example `camelCase` vs `snake_case`. You can setup naming convention to use during serialization and deserialization. Follow the steps described in [configuring naming convention](#configuring-naming-convention) section.
|
|
1448
1974
|
|
|
1449
|
-
###
|
|
1975
|
+
### Enum types
|
|
1450
1976
|
|
|
1451
|
-
|
|
1977
|
+
Enum types are supported but require special handling. This is because of how enums are represented after compiling them to `JavaScript`. You can know more about how to define them in a [property decorator](#property-decorator) section. If you are interested about the `Enum` compilation details you can check the official [documentation](https://www.typescriptlang.org/docs/handbook/enums.html).
|
|
1978
|
+
|
|
1979
|
+
### Generic types
|
|
1980
|
+
|
|
1981
|
+
Generic types are supported and you can define them. Read [property decorator](#property-decorator) section for more info.
|
|
1452
1982
|
|
|
1453
1983
|
### Integration with Angular
|
|
1454
1984
|
|
|
@@ -1456,15 +1986,19 @@ With `Angular` you do not need to install [reflect-metadata](https://github.com/
|
|
|
1456
1986
|
|
|
1457
1987
|
To make `Angular` injector work for you a custom `Injector` needs to be implemented. Check [defining custom injector](#defining-custom-injector) section for more info.
|
|
1458
1988
|
|
|
1989
|
+
### Polymorphic types
|
|
1990
|
+
|
|
1991
|
+
Polymorphic types are supported. In most cases additional configuration is required. Check [configuring usage of polymorphic types](#configuring-usage-of-polymorphic-types) section for more info.
|
|
1992
|
+
|
|
1459
1993
|
## Notes
|
|
1460
1994
|
|
|
1461
1995
|
See information about breaking changes, release notes and migration steps between versions [here](https://github.com/dipscope/TypeManager.TS/blob/master/CHANGELOG.md).
|
|
1462
1996
|
|
|
1463
|
-
Thanks for checking this package.
|
|
1997
|
+
Thanks for checking this package.
|
|
1464
1998
|
|
|
1465
|
-
|
|
1999
|
+
Feel free to create an issue if you find any mistakes in documentation or have any improvements in mind.
|
|
1466
2000
|
|
|
1467
|
-
|
|
2001
|
+
We wish you good luck and happy coding!
|
|
1468
2002
|
|
|
1469
2003
|
## License
|
|
1470
2004
|
|