@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +653 -119
  3. package/core/discriminant.d.ts +8 -0
  4. package/core/discriminator.d.ts +6 -0
  5. package/core/fn.d.ts +21 -11
  6. package/core/generic-argument.d.ts +1 -1
  7. package/core/generic-metadata.d.ts +1 -1
  8. package/core/index.d.ts +8 -0
  9. package/core/index.js +2 -2
  10. package/core/inject-index.d.ts +6 -0
  11. package/core/inject-metadata.d.ts +15 -14
  12. package/core/inject-options.d.ts +5 -5
  13. package/core/log.d.ts +6 -6
  14. package/core/metadata.d.ts +3 -3
  15. package/core/property-metadata.d.ts +24 -36
  16. package/core/property-name.d.ts +6 -0
  17. package/core/property-options.d.ts +58 -2
  18. package/core/serializer-context-options.d.ts +6 -6
  19. package/core/serializer-context.d.ts +65 -6
  20. package/core/type-abstraction.d.ts +8 -0
  21. package/core/type-argument.d.ts +2 -2
  22. package/core/type-ctor.d.ts +1 -1
  23. package/core/type-fn.d.ts +8 -0
  24. package/core/type-like.d.ts +1 -1
  25. package/core/type-metadata-symbol.d.ts +6 -0
  26. package/core/type-metadata.d.ts +118 -28
  27. package/core/type-name.d.ts +6 -0
  28. package/core/type-options-base.d.ts +15 -8
  29. package/core/type-options.d.ts +21 -5
  30. package/core/type-resolver.d.ts +2 -2
  31. package/discriminant.d.ts +11 -0
  32. package/discriminator.d.ts +11 -0
  33. package/factories/index.js +2 -2
  34. package/factory.d.ts +4 -4
  35. package/index.d.ts +3 -3
  36. package/index.js +2 -2
  37. package/inject.d.ts +3 -3
  38. package/injector.d.ts +3 -3
  39. package/injectors/index.js +2 -2
  40. package/naming-conventions/index.js +2 -2
  41. package/naming-conventions/pascal-case.naming-convention.d.ts +1 -1
  42. package/package.json +1 -1
  43. package/preserve-discriminator.d.ts +10 -0
  44. package/property.d.ts +3 -3
  45. package/reference-handlers/index.js +2 -2
  46. package/reference-handlers/path.reference-handler.d.ts +22 -6
  47. package/serializers/index.js +2 -2
  48. package/type-manager-options.d.ts +3 -3
  49. package/type-manager.d.ts +217 -46
  50. package/inject-artisan.d.ts +0 -20
  51. package/property-artisan.d.ts +0 -20
  52. package/type-artisan.d.ts +0 -95
package/README.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  ![GitHub](https://img.shields.io/github/license/dipscope/TypeManager.TS) ![NPM](https://img.shields.io/npm/v/@dipscope/type-manager)
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
- * [Serializable and deserializable decorator](#serializable-and-deserializable-decorator)
24
- * [Default value decorator](#default-value-decorator)
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
- * [Generic types](#generic-types)
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
- * [Circular object references](#circular-object-references)
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 = 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 `string` in JSON but `User` class declares it as a `Date` so we also have to perform appropriate conversion when assigning this property.
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 configuration;
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
- ## Defining decorators
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
- 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. To process something you have to call static method of `TypeManager` with providing a type and data you want to work with.
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 = TypeManager.deserialize(User, userObject);
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 = TypeManager.parse(User, userJson);
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 = userManager.serialize(new User());
209
- const user = userManager.deserialize(userObject);
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
- Besides base functionality for serialization and deserialization this allows you to get metadata.
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 the separate section.
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 `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.
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. That 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.
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 the 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.
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
- Handling relation types not differs from built in property types, so if you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) the definition can be the following.
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 prop: string;
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 the separate section.
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
- To define an alias you can use `Alias` decorator. It can be used both on class and property.
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
- Custom data decorator can be used to provide any custom data for type or property.
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 injectors and used accordingly. Read more about [defining custom data](#defining-custom-data) in a separate section.
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
- ### Factory decorator
697
+ ### Default value decorator
590
698
 
591
- Factory decorator can be used to register a handler which should be used for constructing a type instead of default one.
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, Factory } from '@dipscope/type-manager';
702
+ import { Type, Property, DefaultValue } from '@dipscope/type-manager';
595
703
 
596
704
  @Type()
597
- @Factory(new UserFactory())
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
- factory: new UserFactory()
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
- 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.
785
+ You can read more about handling of polymorphic types in this [section](#configuring-usage-of-polymorphic-types).
619
786
 
620
- ### Serializer decorator
787
+ ### Discriminator decorator
621
788
 
622
- Serializer decorator is used to define custom serializer for your type or property.
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, Serializer } from '@dipscope/type-manager';
792
+ import { Type, Property, Discriminator } from '@dipscope/type-manager';
626
793
 
627
794
  @Type()
628
- @Serializer(new UserSerializer())
795
+ @Discriminator('__typename__')
629
796
  export class User
630
797
  {
631
- @Property(String) @Serializer(new UserNameSerializer()) public name: 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
- serializer: new UserSerializer()
808
+ discriminator: '__typename__'
642
809
  })
643
810
  export class User
644
811
  {
645
- @Property(String, { serializer: new UserNameSerializer() }) public name: string;
812
+ @Property(String) public name: string;
646
813
  }
647
814
  ```
648
815
 
649
- Custom serializer should be an implementation of `Serializer` interface. You can read more about [creating a custom serializer](#defining-custom-serializer) in the separate section.
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
- ### Injector decorator
818
+ ### Factory decorator
652
819
 
653
- Injector decorator can be used to define a custom injector implementation which should be used in a type scope.
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, Injector } from '@dipscope/type-manager';
823
+ import { Type, Property, Factory } from '@dipscope/type-manager';
657
824
 
658
825
  @Type()
659
- @Injector(new UserInjector())
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
- injector: new UserInjector()
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
- 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 the separate section.
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
- Injectable decorator is used to register a type in dependency injection container.
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 prop: string;
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 prop: string;
874
+ public property: string;
707
875
  }
708
876
  ```
709
877
 
710
- This type later can be provided as a dependency.
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 the separate section.
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
- ### Serializable and deserializable decorator
930
+ ### Naming convention decorator
732
931
 
733
- This two are used to enable or disable serialization for a certain property.
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, Serializable, Deserializable } from '@dipscope/type-manager';
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 and deserializable.
1053
+ By default all properties are serializable.
760
1054
 
761
- ### Default value decorator
1055
+ ### Serializer decorator
762
1056
 
763
- This decorator is used to define a default value when one is undefined. It can be used on type or property.
1057
+ This decorator is used to define custom serializer for a type or property.
764
1058
 
765
1059
  ```typescript
766
- import { Type, Property, DefaultValue } from '@dipscope/type-manager';
1060
+ import { Type, Property, Serializer } from '@dipscope/type-manager';
767
1061
 
768
1062
  @Type()
769
- @DefaultValue(() => new User())
1063
+ @Serializer(new UserSerializer())
770
1064
  export class User
771
1065
  {
772
- @Property(String) @DefaultValue('BestName') public name: 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
- defaultValue: () => new User()
1076
+ serializer: new UserSerializer()
783
1077
  })
784
1078
  export class User
785
1079
  {
786
- @Property(String, { defaultValue: 'BestName' }) public name: string;
1080
+ @Property(String, { serializer: new UserNameSerializer() }) public name: string;
787
1081
  }
788
1082
  ```
789
1083
 
790
- 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.
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 - 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.
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 `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.
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 - you want to configure some options globally. In this case you can define the complete configuration through special static configure methods.
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, TypeCtor, PropertyOptions } from '@dipscope/type-manager/core';
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<string, PropertyOptions<any>>(
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<TypeCtor<any>, TypeOptions<any>>(
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 the separate section.
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 userManager = new TypeManager(User);
1090
- const user = new User();
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 = userManager.serialize(user);
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 currently 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.
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 circular reference will be handled using JSONPath so you are completely free from errors during conversion to a string.
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 injectors and perform specific actions. Besides pipeline you can get this data in any place you want using `TypeManager` instance.
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 userManager = new TypeManager(User);
1157
- const userMetadata = userManager.typeMetadata;
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 JS object instance into plain JSON object. Deserialize method is called during backward conversion. Here is an example of possible definition for custom `DateTime` class.
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 your 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.
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.typeCtor);
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 = typeMetadata.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 concrete 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.
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
- ### Generic types
1963
+ ### Circular object references
1438
1964
 
1439
- Generic types are supported but you have to properly define them. Read [property decorator](#property-decorator) section for more info.
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. You can setup naming convention to use during serialization and deserialization. Follow the steps described in [configuring naming convention](#configuring-naming-convention) section.
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
- ### Circular object references
1975
+ ### Enum types
1450
1976
 
1451
- 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.
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. If you like it please give repo a star.
1997
+ Thanks for checking this package.
1464
1998
 
1465
- Have any improvements in mind? Feel free to create an issue.
1999
+ Feel free to create an issue if you find any mistakes in documentation or have any improvements in mind.
1466
2000
 
1467
- I wish you good luck and happy coding! ;)
2001
+ We wish you good luck and happy coding!
1468
2002
 
1469
2003
  ## License
1470
2004