@dipscope/type-manager 5.0.0 → 7.0.0-beta.0

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 (776) hide show
  1. package/CHANGELOG.md +262 -224
  2. package/CONTRIBUTING.md +58 -0
  3. package/LICENSE.md +201 -201
  4. package/README.md +1901 -1857
  5. package/dist/es2015/alias.js +2 -0
  6. package/dist/es2015/alias.js.map +1 -0
  7. package/dist/es2015/any.js +3 -0
  8. package/dist/es2015/any.js.map +1 -0
  9. package/dist/es2015/custom-context.js +58 -0
  10. package/dist/es2015/custom-context.js.map +1 -0
  11. package/dist/es2015/custom-key.js +8 -0
  12. package/dist/es2015/custom-key.js.map +1 -0
  13. package/dist/es2015/custom-option.js +2 -0
  14. package/dist/es2015/custom-option.js.map +1 -0
  15. package/dist/es2015/custom-value-resolver.js +2 -0
  16. package/dist/es2015/custom-value-resolver.js.map +1 -0
  17. package/dist/es2015/custom-value.js +2 -0
  18. package/dist/es2015/custom-value.js.map +1 -0
  19. package/dist/es2015/default-value-resolver.js +2 -0
  20. package/dist/es2015/default-value-resolver.js.map +1 -0
  21. package/dist/es2015/default-value.js +2 -0
  22. package/dist/es2015/default-value.js.map +1 -0
  23. package/dist/es2015/discriminant.js +2 -0
  24. package/dist/es2015/discriminant.js.map +1 -0
  25. package/dist/es2015/discriminator.js +2 -0
  26. package/dist/es2015/discriminator.js.map +1 -0
  27. package/dist/es2015/factories/index.js +2 -0
  28. package/dist/es2015/factories/index.js.map +1 -0
  29. package/dist/es2015/factories/type-factory.js +34 -0
  30. package/dist/es2015/factories/type-factory.js.map +1 -0
  31. package/dist/es2015/factory.js +2 -0
  32. package/dist/es2015/factory.js.map +1 -0
  33. package/dist/es2015/functions/get-own-reflect-metadata.js +8 -0
  34. package/dist/es2015/functions/get-own-reflect-metadata.js.map +1 -0
  35. package/dist/es2015/functions/get-reflect-metadata.js +8 -0
  36. package/dist/es2015/functions/get-reflect-metadata.js.map +1 -0
  37. package/dist/es2015/functions/get-words.js +5 -0
  38. package/dist/es2015/functions/get-words.js.map +1 -0
  39. package/{functions/index.d.ts → dist/es2015/functions/index.js} +19 -16
  40. package/dist/es2015/functions/index.js.map +1 -0
  41. package/dist/es2015/functions/is-arrow-function.js +5 -0
  42. package/dist/es2015/functions/is-arrow-function.js.map +1 -0
  43. package/dist/es2015/functions/is-ctor-function.js +5 -0
  44. package/dist/es2015/functions/is-ctor-function.js.map +1 -0
  45. package/dist/es2015/functions/is-data-view.js +4 -0
  46. package/dist/es2015/functions/is-data-view.js.map +1 -0
  47. package/dist/es2015/functions/is-float-32-array.js +4 -0
  48. package/dist/es2015/functions/is-float-32-array.js.map +1 -0
  49. package/dist/es2015/functions/is-float-64-array.js +4 -0
  50. package/dist/es2015/functions/is-float-64-array.js.map +1 -0
  51. package/dist/es2015/functions/is-int-16-array.js +4 -0
  52. package/dist/es2015/functions/is-int-16-array.js.map +1 -0
  53. package/dist/es2015/functions/is-int-32-array.js +4 -0
  54. package/dist/es2015/functions/is-int-32-array.js.map +1 -0
  55. package/dist/es2015/functions/is-int-8-array.js +4 -0
  56. package/dist/es2015/functions/is-int-8-array.js.map +1 -0
  57. package/dist/es2015/functions/is-uint-16-array.js +4 -0
  58. package/dist/es2015/functions/is-uint-16-array.js.map +1 -0
  59. package/dist/es2015/functions/is-uint-32-array.js +4 -0
  60. package/dist/es2015/functions/is-uint-32-array.js.map +1 -0
  61. package/dist/es2015/functions/is-uint-8-array.js +4 -0
  62. package/dist/es2015/functions/is-uint-8-array.js.map +1 -0
  63. package/dist/es2015/functions/is-uint-8-clamped-array.js +4 -0
  64. package/dist/es2015/functions/is-uint-8-clamped-array.js.map +1 -0
  65. package/dist/es2015/functions/json-parse.js +4 -0
  66. package/dist/es2015/functions/json-parse.js.map +1 -0
  67. package/dist/es2015/functions/json-stringify.js +67 -0
  68. package/dist/es2015/functions/json-stringify.js.map +1 -0
  69. package/dist/es2015/functions/name-of.js +8 -0
  70. package/dist/es2015/functions/name-of.js.map +1 -0
  71. package/dist/es2015/generic-argument.js +2 -0
  72. package/dist/es2015/generic-argument.js.map +1 -0
  73. package/dist/es2015/generic-metadata-resolver.js +2 -0
  74. package/dist/es2015/generic-metadata-resolver.js.map +1 -0
  75. package/dist/es2015/generic-metadata.js +2 -0
  76. package/dist/es2015/generic-metadata.js.map +1 -0
  77. package/dist/es2015/generic-structure.js +2 -0
  78. package/dist/es2015/generic-structure.js.map +1 -0
  79. package/{index.d.ts → dist/es2015/index.js} +71 -54
  80. package/dist/es2015/index.js.map +1 -0
  81. package/dist/es2015/inject-index.js +2 -0
  82. package/dist/es2015/inject-index.js.map +1 -0
  83. package/dist/es2015/inject-internals.js +2 -0
  84. package/dist/es2015/inject-internals.js.map +1 -0
  85. package/dist/es2015/inject-metadata.js +63 -0
  86. package/dist/es2015/inject-metadata.js.map +1 -0
  87. package/dist/es2015/inject-options.js +2 -0
  88. package/dist/es2015/inject-options.js.map +1 -0
  89. package/dist/es2015/inject-sorter.js +2 -0
  90. package/dist/es2015/inject-sorter.js.map +1 -0
  91. package/dist/es2015/inject-sorters/asc-inject-sorter.js +14 -0
  92. package/dist/es2015/inject-sorters/asc-inject-sorter.js.map +1 -0
  93. package/dist/es2015/inject-sorters/desc-inject-sorter.js +14 -0
  94. package/dist/es2015/inject-sorters/desc-inject-sorter.js.map +1 -0
  95. package/dist/es2015/inject-sorters/index.js +3 -0
  96. package/dist/es2015/inject-sorters/index.js.map +1 -0
  97. package/dist/es2015/inject.js +24 -0
  98. package/dist/es2015/inject.js.map +1 -0
  99. package/dist/es2015/injector.js +2 -0
  100. package/dist/es2015/injector.js.map +1 -0
  101. package/{injectors/index.d.ts → dist/es2015/injectors/index.js} +2 -1
  102. package/dist/es2015/injectors/index.js.map +1 -0
  103. package/dist/es2015/injectors/singleton-injector.js +31 -0
  104. package/dist/es2015/injectors/singleton-injector.js.map +1 -0
  105. package/dist/es2015/log-level.js +8 -0
  106. package/dist/es2015/log-level.js.map +1 -0
  107. package/dist/es2015/log.js +50 -0
  108. package/dist/es2015/log.js.map +1 -0
  109. package/dist/es2015/metadata.js +81 -0
  110. package/dist/es2015/metadata.js.map +1 -0
  111. package/dist/es2015/naming-convention.js +2 -0
  112. package/dist/es2015/naming-convention.js.map +1 -0
  113. package/dist/es2015/naming-conventions/camel-case-naming-convention.js +17 -0
  114. package/dist/es2015/naming-conventions/camel-case-naming-convention.js.map +1 -0
  115. package/dist/es2015/naming-conventions/flat-case-naming-convention.js +13 -0
  116. package/dist/es2015/naming-conventions/flat-case-naming-convention.js.map +1 -0
  117. package/dist/es2015/naming-conventions/flat-upper-case-naming-convention.js +13 -0
  118. package/dist/es2015/naming-conventions/flat-upper-case-naming-convention.js.map +1 -0
  119. package/{naming-conventions/index.d.ts → dist/es2015/naming-conventions/index.js} +9 -8
  120. package/dist/es2015/naming-conventions/index.js.map +1 -0
  121. package/dist/es2015/naming-conventions/kebab-case-naming-convention.js +17 -0
  122. package/dist/es2015/naming-conventions/kebab-case-naming-convention.js.map +1 -0
  123. package/dist/es2015/naming-conventions/kebab-upper-case-naming-convention.js +17 -0
  124. package/dist/es2015/naming-conventions/kebab-upper-case-naming-convention.js.map +1 -0
  125. package/dist/es2015/naming-conventions/pascal-case-naming-convention.js +18 -0
  126. package/dist/es2015/naming-conventions/pascal-case-naming-convention.js.map +1 -0
  127. package/dist/es2015/naming-conventions/snake-case-naming-convention.js +17 -0
  128. package/dist/es2015/naming-conventions/snake-case-naming-convention.js.map +1 -0
  129. package/dist/es2015/naming-conventions/snake-upper-case-naming-convention.js +17 -0
  130. package/dist/es2015/naming-conventions/snake-upper-case-naming-convention.js.map +1 -0
  131. package/dist/es2015/property-internals.js +2 -0
  132. package/dist/es2015/property-internals.js.map +1 -0
  133. package/dist/es2015/property-metadata.js +269 -0
  134. package/dist/es2015/property-metadata.js.map +1 -0
  135. package/dist/es2015/property-name.js +2 -0
  136. package/dist/es2015/property-name.js.map +1 -0
  137. package/dist/es2015/property-options.js +2 -0
  138. package/dist/es2015/property-options.js.map +1 -0
  139. package/dist/es2015/property-sorter.js +2 -0
  140. package/dist/es2015/property-sorter.js.map +1 -0
  141. package/dist/es2015/property-sorters/asc-property-sorter.js +14 -0
  142. package/dist/es2015/property-sorters/asc-property-sorter.js.map +1 -0
  143. package/dist/es2015/property-sorters/desc-property-sorter.js +14 -0
  144. package/dist/es2015/property-sorters/desc-property-sorter.js.map +1 -0
  145. package/dist/es2015/property-sorters/index.js +3 -0
  146. package/dist/es2015/property-sorters/index.js.map +1 -0
  147. package/dist/es2015/property.js +39 -0
  148. package/dist/es2015/property.js.map +1 -0
  149. package/dist/es2015/reference-callback.js +2 -0
  150. package/dist/es2015/reference-callback.js.map +1 -0
  151. package/dist/es2015/reference-handler.js +2 -0
  152. package/dist/es2015/reference-handler.js.map +1 -0
  153. package/dist/es2015/reference-handlers/circular-reference-handler.js +36 -0
  154. package/dist/es2015/reference-handlers/circular-reference-handler.js.map +1 -0
  155. package/dist/es2015/reference-handlers/index.js +4 -0
  156. package/dist/es2015/reference-handlers/index.js.map +1 -0
  157. package/dist/es2015/reference-handlers/json-path-reference-handler.js +69 -0
  158. package/dist/es2015/reference-handlers/json-path-reference-handler.js.map +1 -0
  159. package/dist/es2015/reference-handlers/plain-reference-handler.js +32 -0
  160. package/dist/es2015/reference-handlers/plain-reference-handler.js.map +1 -0
  161. package/dist/es2015/reference-key.js +2 -0
  162. package/dist/es2015/reference-key.js.map +1 -0
  163. package/dist/es2015/reference-value-getter.js +2 -0
  164. package/dist/es2015/reference-value-getter.js.map +1 -0
  165. package/dist/es2015/reference-value-setter.js +2 -0
  166. package/dist/es2015/reference-value-setter.js.map +1 -0
  167. package/dist/es2015/reference-value.js +2 -0
  168. package/dist/es2015/reference-value.js.map +1 -0
  169. package/dist/es2015/serializer-context-options.js +2 -0
  170. package/dist/es2015/serializer-context-options.js.map +1 -0
  171. package/dist/es2015/serializer-context.js +282 -0
  172. package/dist/es2015/serializer-context.js.map +1 -0
  173. package/dist/es2015/serializer.js +2 -0
  174. package/dist/es2015/serializer.js.map +1 -0
  175. package/dist/es2015/serializers/any-serializer.js +22 -0
  176. package/dist/es2015/serializers/any-serializer.js.map +1 -0
  177. package/dist/es2015/serializers/array-buffer-serializer.js +41 -0
  178. package/dist/es2015/serializers/array-buffer-serializer.js.map +1 -0
  179. package/dist/es2015/serializers/array-serializer.js +60 -0
  180. package/dist/es2015/serializers/array-serializer.js.map +1 -0
  181. package/dist/es2015/serializers/boolean-serializer.js +55 -0
  182. package/dist/es2015/serializers/boolean-serializer.js.map +1 -0
  183. package/dist/es2015/serializers/data-view-serializer.js +42 -0
  184. package/dist/es2015/serializers/data-view-serializer.js.map +1 -0
  185. package/dist/es2015/serializers/date-serializer.js +34 -0
  186. package/dist/es2015/serializers/date-serializer.js.map +1 -0
  187. package/dist/es2015/serializers/float-32-array-serializer.js +35 -0
  188. package/dist/es2015/serializers/float-32-array-serializer.js.map +1 -0
  189. package/dist/es2015/serializers/float-64-array-serializer.js +35 -0
  190. package/dist/es2015/serializers/float-64-array-serializer.js.map +1 -0
  191. package/{serializers/index.d.ts → dist/es2015/serializers/index.js} +22 -19
  192. package/dist/es2015/serializers/index.js.map +1 -0
  193. package/dist/es2015/serializers/int-16-array-serializer.js +35 -0
  194. package/dist/es2015/serializers/int-16-array-serializer.js.map +1 -0
  195. package/dist/es2015/serializers/int-32-array-serializer.js +35 -0
  196. package/dist/es2015/serializers/int-32-array-serializer.js.map +1 -0
  197. package/dist/es2015/serializers/int-8-array-serializer.js +35 -0
  198. package/dist/es2015/serializers/int-8-array-serializer.js.map +1 -0
  199. package/dist/es2015/serializers/map-serializer.js +96 -0
  200. package/dist/es2015/serializers/map-serializer.js.map +1 -0
  201. package/dist/es2015/serializers/number-serializer.js +49 -0
  202. package/dist/es2015/serializers/number-serializer.js.map +1 -0
  203. package/dist/es2015/serializers/set-serializer.js +62 -0
  204. package/dist/es2015/serializers/set-serializer.js.map +1 -0
  205. package/dist/es2015/serializers/string-serializer.js +55 -0
  206. package/dist/es2015/serializers/string-serializer.js.map +1 -0
  207. package/dist/es2015/serializers/type-serializer.js +121 -0
  208. package/dist/es2015/serializers/type-serializer.js.map +1 -0
  209. package/dist/es2015/serializers/uint-16-array-serializer.js +35 -0
  210. package/dist/es2015/serializers/uint-16-array-serializer.js.map +1 -0
  211. package/dist/es2015/serializers/uint-32-array-serializer.js +35 -0
  212. package/dist/es2015/serializers/uint-32-array-serializer.js.map +1 -0
  213. package/dist/es2015/serializers/uint-8-array-serializer.js +35 -0
  214. package/dist/es2015/serializers/uint-8-array-serializer.js.map +1 -0
  215. package/dist/es2015/serializers/uint-8-clamped-array-serializer.js +35 -0
  216. package/dist/es2015/serializers/uint-8-clamped-array-serializer.js.map +1 -0
  217. package/dist/es2015/serializers/unknown-serializer.js +22 -0
  218. package/dist/es2015/serializers/unknown-serializer.js.map +1 -0
  219. package/dist/es2015/type-abstraction.js +2 -0
  220. package/dist/es2015/type-abstraction.js.map +1 -0
  221. package/dist/es2015/type-argument.js +2 -0
  222. package/dist/es2015/type-argument.js.map +1 -0
  223. package/dist/es2015/type-configuration.js +2 -0
  224. package/dist/es2015/type-configuration.js.map +1 -0
  225. package/dist/es2015/type-context-entry.js +9 -0
  226. package/dist/es2015/type-context-entry.js.map +1 -0
  227. package/dist/es2015/type-context.js +31 -0
  228. package/dist/es2015/type-context.js.map +1 -0
  229. package/dist/es2015/type-ctor.js +2 -0
  230. package/dist/es2015/type-ctor.js.map +1 -0
  231. package/dist/es2015/type-fn.js +2 -0
  232. package/dist/es2015/type-fn.js.map +1 -0
  233. package/dist/es2015/type-internals.js +2 -0
  234. package/dist/es2015/type-internals.js.map +1 -0
  235. package/dist/es2015/type-like.js +2 -0
  236. package/dist/es2015/type-like.js.map +1 -0
  237. package/dist/es2015/type-manager-options.js +2 -0
  238. package/dist/es2015/type-manager-options.js.map +1 -0
  239. package/dist/es2015/type-manager.js +320 -0
  240. package/dist/es2015/type-manager.js.map +1 -0
  241. package/dist/es2015/type-metadata-resolver.js +2 -0
  242. package/dist/es2015/type-metadata-resolver.js.map +1 -0
  243. package/dist/es2015/type-metadata-symbol.js +2 -0
  244. package/dist/es2015/type-metadata-symbol.js.map +1 -0
  245. package/dist/es2015/type-metadata.js +414 -0
  246. package/dist/es2015/type-metadata.js.map +1 -0
  247. package/dist/es2015/type-name.js +2 -0
  248. package/dist/es2015/type-name.js.map +1 -0
  249. package/dist/es2015/type-options-base.js +2 -0
  250. package/dist/es2015/type-options-base.js.map +1 -0
  251. package/dist/es2015/type-options.js +2 -0
  252. package/dist/es2015/type-options.js.map +1 -0
  253. package/dist/es2015/type-resolver.js +2 -0
  254. package/dist/es2015/type-resolver.js.map +1 -0
  255. package/dist/es2015/type.js +8 -0
  256. package/dist/es2015/type.js.map +1 -0
  257. package/dist/es2015/unknown.js +3 -0
  258. package/dist/es2015/unknown.js.map +1 -0
  259. package/dist/es5/alias.js +2 -0
  260. package/dist/es5/alias.js.map +1 -0
  261. package/dist/es5/any.js +7 -0
  262. package/dist/es5/any.js.map +1 -0
  263. package/dist/es5/custom-context.js +94 -0
  264. package/dist/es5/custom-context.js.map +1 -0
  265. package/dist/es5/custom-key.js +10 -0
  266. package/dist/es5/custom-key.js.map +1 -0
  267. package/dist/es5/custom-option.js +2 -0
  268. package/dist/es5/custom-option.js.map +1 -0
  269. package/dist/es5/custom-value-resolver.js +2 -0
  270. package/dist/es5/custom-value-resolver.js.map +1 -0
  271. package/dist/es5/custom-value.js +2 -0
  272. package/dist/es5/custom-value.js.map +1 -0
  273. package/dist/es5/default-value-resolver.js +2 -0
  274. package/dist/es5/default-value-resolver.js.map +1 -0
  275. package/dist/es5/default-value.js +2 -0
  276. package/dist/es5/default-value.js.map +1 -0
  277. package/dist/es5/discriminant.js +2 -0
  278. package/dist/es5/discriminant.js.map +1 -0
  279. package/dist/es5/discriminator.js +2 -0
  280. package/dist/es5/discriminator.js.map +1 -0
  281. package/dist/es5/factories/index.js +2 -0
  282. package/dist/es5/factories/index.js.map +1 -0
  283. package/dist/es5/factories/type-factory.js +60 -0
  284. package/dist/es5/factories/type-factory.js.map +1 -0
  285. package/dist/es5/factory.js +2 -0
  286. package/dist/es5/factory.js.map +1 -0
  287. package/dist/es5/functions/get-own-reflect-metadata.js +8 -0
  288. package/dist/es5/functions/get-own-reflect-metadata.js.map +1 -0
  289. package/dist/es5/functions/get-reflect-metadata.js +8 -0
  290. package/dist/es5/functions/get-reflect-metadata.js.map +1 -0
  291. package/dist/es5/functions/get-words.js +5 -0
  292. package/dist/es5/functions/get-words.js.map +1 -0
  293. package/dist/es5/functions/index.js +19 -0
  294. package/dist/es5/functions/index.js.map +1 -0
  295. package/dist/es5/functions/is-arrow-function.js +5 -0
  296. package/dist/es5/functions/is-arrow-function.js.map +1 -0
  297. package/dist/es5/functions/is-ctor-function.js +5 -0
  298. package/dist/es5/functions/is-ctor-function.js.map +1 -0
  299. package/dist/es5/functions/is-data-view.js +4 -0
  300. package/dist/es5/functions/is-data-view.js.map +1 -0
  301. package/dist/es5/functions/is-float-32-array.js +4 -0
  302. package/dist/es5/functions/is-float-32-array.js.map +1 -0
  303. package/dist/es5/functions/is-float-64-array.js +4 -0
  304. package/dist/es5/functions/is-float-64-array.js.map +1 -0
  305. package/dist/es5/functions/is-int-16-array.js +4 -0
  306. package/dist/es5/functions/is-int-16-array.js.map +1 -0
  307. package/dist/es5/functions/is-int-32-array.js +4 -0
  308. package/dist/es5/functions/is-int-32-array.js.map +1 -0
  309. package/dist/es5/functions/is-int-8-array.js +4 -0
  310. package/dist/es5/functions/is-int-8-array.js.map +1 -0
  311. package/dist/es5/functions/is-uint-16-array.js +4 -0
  312. package/dist/es5/functions/is-uint-16-array.js.map +1 -0
  313. package/dist/es5/functions/is-uint-32-array.js +4 -0
  314. package/dist/es5/functions/is-uint-32-array.js.map +1 -0
  315. package/dist/es5/functions/is-uint-8-array.js +4 -0
  316. package/dist/es5/functions/is-uint-8-array.js.map +1 -0
  317. package/dist/es5/functions/is-uint-8-clamped-array.js +4 -0
  318. package/dist/es5/functions/is-uint-8-clamped-array.js.map +1 -0
  319. package/dist/es5/functions/json-parse.js +4 -0
  320. package/dist/es5/functions/json-parse.js.map +1 -0
  321. package/dist/es5/functions/json-stringify.js +67 -0
  322. package/dist/es5/functions/json-stringify.js.map +1 -0
  323. package/dist/es5/functions/name-of.js +8 -0
  324. package/dist/es5/functions/name-of.js.map +1 -0
  325. package/dist/es5/generic-argument.js +2 -0
  326. package/dist/es5/generic-argument.js.map +1 -0
  327. package/dist/es5/generic-metadata-resolver.js +2 -0
  328. package/dist/es5/generic-metadata-resolver.js.map +1 -0
  329. package/dist/es5/generic-metadata.js +2 -0
  330. package/dist/es5/generic-metadata.js.map +1 -0
  331. package/dist/es5/generic-structure.js +2 -0
  332. package/dist/es5/generic-structure.js.map +1 -0
  333. package/dist/es5/index.js +71 -0
  334. package/dist/es5/index.js.map +1 -0
  335. package/dist/es5/inject-index.js +2 -0
  336. package/dist/es5/inject-index.js.map +1 -0
  337. package/dist/es5/inject-internals.js +2 -0
  338. package/dist/es5/inject-internals.js.map +1 -0
  339. package/dist/es5/inject-metadata.js +84 -0
  340. package/dist/es5/inject-metadata.js.map +1 -0
  341. package/dist/es5/inject-options.js +2 -0
  342. package/dist/es5/inject-options.js.map +1 -0
  343. package/dist/es5/inject-sorter.js +2 -0
  344. package/dist/es5/inject-sorter.js.map +1 -0
  345. package/dist/es5/inject-sorters/asc-inject-sorter.js +18 -0
  346. package/dist/es5/inject-sorters/asc-inject-sorter.js.map +1 -0
  347. package/dist/es5/inject-sorters/desc-inject-sorter.js +18 -0
  348. package/dist/es5/inject-sorters/desc-inject-sorter.js.map +1 -0
  349. package/dist/es5/inject-sorters/index.js +3 -0
  350. package/dist/es5/inject-sorters/index.js.map +1 -0
  351. package/dist/es5/inject.js +24 -0
  352. package/dist/es5/inject.js.map +1 -0
  353. package/dist/es5/injector.js +2 -0
  354. package/dist/es5/injector.js.map +1 -0
  355. package/dist/es5/injectors/index.js +2 -0
  356. package/dist/es5/injectors/index.js.map +1 -0
  357. package/dist/es5/injectors/singleton-injector.js +45 -0
  358. package/dist/es5/injectors/singleton-injector.js.map +1 -0
  359. package/dist/es5/log-level.js +8 -0
  360. package/dist/es5/log-level.js.map +1 -0
  361. package/dist/es5/log.js +65 -0
  362. package/dist/es5/log.js.map +1 -0
  363. package/dist/es5/metadata.js +106 -0
  364. package/dist/es5/metadata.js.map +1 -0
  365. package/dist/es5/naming-convention.js +2 -0
  366. package/dist/es5/naming-convention.js.map +1 -0
  367. package/dist/es5/naming-conventions/camel-case-naming-convention.js +21 -0
  368. package/dist/es5/naming-conventions/camel-case-naming-convention.js.map +1 -0
  369. package/dist/es5/naming-conventions/flat-case-naming-convention.js +17 -0
  370. package/dist/es5/naming-conventions/flat-case-naming-convention.js.map +1 -0
  371. package/dist/es5/naming-conventions/flat-upper-case-naming-convention.js +17 -0
  372. package/dist/es5/naming-conventions/flat-upper-case-naming-convention.js.map +1 -0
  373. package/dist/es5/naming-conventions/index.js +9 -0
  374. package/dist/es5/naming-conventions/index.js.map +1 -0
  375. package/dist/es5/naming-conventions/kebab-case-naming-convention.js +21 -0
  376. package/dist/es5/naming-conventions/kebab-case-naming-convention.js.map +1 -0
  377. package/dist/es5/naming-conventions/kebab-upper-case-naming-convention.js +21 -0
  378. package/dist/es5/naming-conventions/kebab-upper-case-naming-convention.js.map +1 -0
  379. package/dist/es5/naming-conventions/pascal-case-naming-convention.js +22 -0
  380. package/dist/es5/naming-conventions/pascal-case-naming-convention.js.map +1 -0
  381. package/dist/es5/naming-conventions/snake-case-naming-convention.js +21 -0
  382. package/dist/es5/naming-conventions/snake-case-naming-convention.js.map +1 -0
  383. package/dist/es5/naming-conventions/snake-upper-case-naming-convention.js +21 -0
  384. package/dist/es5/naming-conventions/snake-upper-case-naming-convention.js.map +1 -0
  385. package/dist/es5/property-internals.js +2 -0
  386. package/dist/es5/property-internals.js.map +1 -0
  387. package/dist/es5/property-metadata.js +378 -0
  388. package/dist/es5/property-metadata.js.map +1 -0
  389. package/dist/es5/property-name.js +2 -0
  390. package/dist/es5/property-name.js.map +1 -0
  391. package/dist/es5/property-options.js +2 -0
  392. package/dist/es5/property-options.js.map +1 -0
  393. package/dist/es5/property-sorter.js +2 -0
  394. package/dist/es5/property-sorter.js.map +1 -0
  395. package/dist/es5/property-sorters/asc-property-sorter.js +18 -0
  396. package/dist/es5/property-sorters/asc-property-sorter.js.map +1 -0
  397. package/dist/es5/property-sorters/desc-property-sorter.js +18 -0
  398. package/dist/es5/property-sorters/desc-property-sorter.js.map +1 -0
  399. package/dist/es5/property-sorters/index.js +3 -0
  400. package/dist/es5/property-sorters/index.js.map +1 -0
  401. package/dist/es5/property.js +39 -0
  402. package/dist/es5/property.js.map +1 -0
  403. package/dist/es5/reference-callback.js +2 -0
  404. package/dist/es5/reference-callback.js.map +1 -0
  405. package/dist/es5/reference-handler.js +2 -0
  406. package/dist/es5/reference-handler.js.map +1 -0
  407. package/dist/es5/reference-handlers/circular-reference-handler.js +40 -0
  408. package/dist/es5/reference-handlers/circular-reference-handler.js.map +1 -0
  409. package/dist/es5/reference-handlers/index.js +4 -0
  410. package/dist/es5/reference-handlers/index.js.map +1 -0
  411. package/dist/es5/reference-handlers/json-path-reference-handler.js +93 -0
  412. package/dist/es5/reference-handlers/json-path-reference-handler.js.map +1 -0
  413. package/dist/es5/reference-handlers/plain-reference-handler.js +36 -0
  414. package/dist/es5/reference-handlers/plain-reference-handler.js.map +1 -0
  415. package/dist/es5/reference-key.js +2 -0
  416. package/dist/es5/reference-key.js.map +1 -0
  417. package/dist/es5/reference-value-getter.js +2 -0
  418. package/dist/es5/reference-value-getter.js.map +1 -0
  419. package/dist/es5/reference-value-setter.js +2 -0
  420. package/dist/es5/reference-value-setter.js.map +1 -0
  421. package/dist/es5/reference-value.js +2 -0
  422. package/dist/es5/reference-value.js.map +1 -0
  423. package/dist/es5/serializer-context-options.js +2 -0
  424. package/dist/es5/serializer-context-options.js.map +1 -0
  425. package/dist/es5/serializer-context.js +417 -0
  426. package/dist/es5/serializer-context.js.map +1 -0
  427. package/dist/es5/serializer.js +2 -0
  428. package/dist/es5/serializer.js.map +1 -0
  429. package/dist/es5/serializers/any-serializer.js +26 -0
  430. package/dist/es5/serializers/any-serializer.js.map +1 -0
  431. package/dist/es5/serializers/array-buffer-serializer.js +45 -0
  432. package/dist/es5/serializers/array-buffer-serializer.js.map +1 -0
  433. package/dist/es5/serializers/array-serializer.js +70 -0
  434. package/dist/es5/serializers/array-serializer.js.map +1 -0
  435. package/dist/es5/serializers/boolean-serializer.js +59 -0
  436. package/dist/es5/serializers/boolean-serializer.js.map +1 -0
  437. package/dist/es5/serializers/data-view-serializer.js +46 -0
  438. package/dist/es5/serializers/data-view-serializer.js.map +1 -0
  439. package/dist/es5/serializers/date-serializer.js +38 -0
  440. package/dist/es5/serializers/date-serializer.js.map +1 -0
  441. package/dist/es5/serializers/float-32-array-serializer.js +39 -0
  442. package/dist/es5/serializers/float-32-array-serializer.js.map +1 -0
  443. package/dist/es5/serializers/float-64-array-serializer.js +39 -0
  444. package/dist/es5/serializers/float-64-array-serializer.js.map +1 -0
  445. package/dist/es5/serializers/index.js +22 -0
  446. package/dist/es5/serializers/index.js.map +1 -0
  447. package/dist/es5/serializers/int-16-array-serializer.js +39 -0
  448. package/dist/es5/serializers/int-16-array-serializer.js.map +1 -0
  449. package/dist/es5/serializers/int-32-array-serializer.js +39 -0
  450. package/dist/es5/serializers/int-32-array-serializer.js.map +1 -0
  451. package/dist/es5/serializers/int-8-array-serializer.js +39 -0
  452. package/dist/es5/serializers/int-8-array-serializer.js.map +1 -0
  453. package/dist/es5/serializers/map-serializer.js +115 -0
  454. package/dist/es5/serializers/map-serializer.js.map +1 -0
  455. package/dist/es5/serializers/number-serializer.js +53 -0
  456. package/dist/es5/serializers/number-serializer.js.map +1 -0
  457. package/dist/es5/serializers/set-serializer.js +78 -0
  458. package/dist/es5/serializers/set-serializer.js.map +1 -0
  459. package/dist/es5/serializers/string-serializer.js +59 -0
  460. package/dist/es5/serializers/string-serializer.js.map +1 -0
  461. package/dist/es5/serializers/type-serializer.js +154 -0
  462. package/dist/es5/serializers/type-serializer.js.map +1 -0
  463. package/dist/es5/serializers/uint-16-array-serializer.js +39 -0
  464. package/dist/es5/serializers/uint-16-array-serializer.js.map +1 -0
  465. package/dist/es5/serializers/uint-32-array-serializer.js +39 -0
  466. package/dist/es5/serializers/uint-32-array-serializer.js.map +1 -0
  467. package/dist/es5/serializers/uint-8-array-serializer.js +39 -0
  468. package/dist/es5/serializers/uint-8-array-serializer.js.map +1 -0
  469. package/dist/es5/serializers/uint-8-clamped-array-serializer.js +39 -0
  470. package/dist/es5/serializers/uint-8-clamped-array-serializer.js.map +1 -0
  471. package/dist/es5/serializers/unknown-serializer.js +26 -0
  472. package/dist/es5/serializers/unknown-serializer.js.map +1 -0
  473. package/dist/es5/type-abstraction.js +2 -0
  474. package/dist/es5/type-abstraction.js.map +1 -0
  475. package/dist/es5/type-argument.js +2 -0
  476. package/dist/es5/type-argument.js.map +1 -0
  477. package/dist/es5/type-configuration.js +2 -0
  478. package/dist/es5/type-configuration.js.map +1 -0
  479. package/dist/es5/type-context-entry.js +11 -0
  480. package/dist/es5/type-context-entry.js.map +1 -0
  481. package/dist/es5/type-context.js +33 -0
  482. package/dist/es5/type-context.js.map +1 -0
  483. package/dist/es5/type-ctor.js +2 -0
  484. package/dist/es5/type-ctor.js.map +1 -0
  485. package/dist/es5/type-fn.js +2 -0
  486. package/dist/es5/type-fn.js.map +1 -0
  487. package/dist/es5/type-internals.js +2 -0
  488. package/dist/es5/type-internals.js.map +1 -0
  489. package/dist/es5/type-like.js +2 -0
  490. package/dist/es5/type-like.js.map +1 -0
  491. package/dist/es5/type-manager-options.js +2 -0
  492. package/dist/es5/type-manager-options.js.map +1 -0
  493. package/dist/es5/type-manager.js +413 -0
  494. package/dist/es5/type-manager.js.map +1 -0
  495. package/dist/es5/type-metadata-resolver.js +2 -0
  496. package/dist/es5/type-metadata-resolver.js.map +1 -0
  497. package/dist/es5/type-metadata-symbol.js +2 -0
  498. package/dist/es5/type-metadata-symbol.js.map +1 -0
  499. package/dist/es5/type-metadata.js +564 -0
  500. package/dist/es5/type-metadata.js.map +1 -0
  501. package/dist/es5/type-name.js +2 -0
  502. package/dist/es5/type-name.js.map +1 -0
  503. package/dist/es5/type-options-base.js +2 -0
  504. package/dist/es5/type-options-base.js.map +1 -0
  505. package/dist/es5/type-options.js +2 -0
  506. package/dist/es5/type-options.js.map +1 -0
  507. package/dist/es5/type-resolver.js +2 -0
  508. package/dist/es5/type-resolver.js.map +1 -0
  509. package/dist/es5/type.js +8 -0
  510. package/dist/es5/type.js.map +1 -0
  511. package/dist/es5/unknown.js +7 -0
  512. package/dist/es5/unknown.js.map +1 -0
  513. package/{alias.d.ts → dist/types/alias.d.ts} +7 -6
  514. package/dist/types/alias.d.ts.map +1 -0
  515. package/dist/types/any.d.ts +10 -0
  516. package/dist/types/any.d.ts.map +1 -0
  517. package/dist/types/custom-context.d.ts +89 -0
  518. package/dist/types/custom-context.d.ts.map +1 -0
  519. package/dist/types/custom-key.d.ts +29 -0
  520. package/dist/types/custom-key.d.ts.map +1 -0
  521. package/dist/types/custom-option.d.ts +11 -0
  522. package/dist/types/custom-option.d.ts.map +1 -0
  523. package/dist/types/custom-value-resolver.d.ts +7 -0
  524. package/dist/types/custom-value-resolver.d.ts.map +1 -0
  525. package/dist/types/custom-value.d.ts +7 -0
  526. package/dist/types/custom-value.d.ts.map +1 -0
  527. package/{default-value-resolver.d.ts → dist/types/default-value-resolver.d.ts} +8 -7
  528. package/dist/types/default-value-resolver.d.ts.map +1 -0
  529. package/{default-value.d.ts → dist/types/default-value.d.ts} +8 -7
  530. package/dist/types/default-value.d.ts.map +1 -0
  531. package/{discriminant.d.ts → dist/types/discriminant.d.ts} +9 -8
  532. package/dist/types/discriminant.d.ts.map +1 -0
  533. package/{discriminator.d.ts → dist/types/discriminator.d.ts} +7 -6
  534. package/dist/types/discriminator.d.ts.map +1 -0
  535. package/dist/types/factories/index.d.ts +2 -0
  536. package/dist/types/factories/index.d.ts.map +1 -0
  537. package/{factories → dist/types/factories}/type-factory.d.ts +20 -19
  538. package/dist/types/factories/type-factory.d.ts.map +1 -0
  539. package/{factory.d.ts → dist/types/factory.d.ts} +19 -18
  540. package/dist/types/factory.d.ts.map +1 -0
  541. package/{functions → dist/types/functions}/get-own-reflect-metadata.d.ts +10 -9
  542. package/dist/types/functions/get-own-reflect-metadata.d.ts.map +1 -0
  543. package/{functions → dist/types/functions}/get-reflect-metadata.d.ts +11 -10
  544. package/dist/types/functions/get-reflect-metadata.d.ts.map +1 -0
  545. package/{functions → dist/types/functions}/get-words.d.ts +9 -8
  546. package/dist/types/functions/get-words.d.ts.map +1 -0
  547. package/dist/types/functions/index.d.ts +19 -0
  548. package/dist/types/functions/index.d.ts.map +1 -0
  549. package/{functions → dist/types/functions}/is-arrow-function.d.ts +9 -8
  550. package/dist/types/functions/is-arrow-function.d.ts.map +1 -0
  551. package/{functions → dist/types/functions}/is-ctor-function.d.ts +9 -8
  552. package/dist/types/functions/is-ctor-function.d.ts.map +1 -0
  553. package/{functions → dist/types/functions}/is-data-view.d.ts +9 -8
  554. package/dist/types/functions/is-data-view.d.ts.map +1 -0
  555. package/{functions → dist/types/functions}/is-float-32-array.d.ts +9 -8
  556. package/dist/types/functions/is-float-32-array.d.ts.map +1 -0
  557. package/{functions → dist/types/functions}/is-float-64-array.d.ts +9 -8
  558. package/dist/types/functions/is-float-64-array.d.ts.map +1 -0
  559. package/{functions → dist/types/functions}/is-int-16-array.d.ts +9 -8
  560. package/dist/types/functions/is-int-16-array.d.ts.map +1 -0
  561. package/{functions → dist/types/functions}/is-int-32-array.d.ts +9 -8
  562. package/dist/types/functions/is-int-32-array.d.ts.map +1 -0
  563. package/{functions → dist/types/functions}/is-int-8-array.d.ts +9 -8
  564. package/dist/types/functions/is-int-8-array.d.ts.map +1 -0
  565. package/{functions → dist/types/functions}/is-uint-16-array.d.ts +9 -8
  566. package/dist/types/functions/is-uint-16-array.d.ts.map +1 -0
  567. package/{functions → dist/types/functions}/is-uint-32-array.d.ts +9 -8
  568. package/dist/types/functions/is-uint-32-array.d.ts.map +1 -0
  569. package/{functions → dist/types/functions}/is-uint-8-array.d.ts +9 -8
  570. package/dist/types/functions/is-uint-8-array.d.ts.map +1 -0
  571. package/{functions → dist/types/functions}/is-uint-8-clamped-array.d.ts +9 -8
  572. package/dist/types/functions/is-uint-8-clamped-array.d.ts.map +1 -0
  573. package/dist/types/functions/json-parse.d.ts +10 -0
  574. package/dist/types/functions/json-parse.d.ts.map +1 -0
  575. package/dist/types/functions/json-stringify.d.ts +11 -0
  576. package/dist/types/functions/json-stringify.d.ts.map +1 -0
  577. package/{functions → dist/types/functions}/name-of.d.ts +9 -8
  578. package/dist/types/functions/name-of.d.ts.map +1 -0
  579. package/{generic-argument.d.ts → dist/types/generic-argument.d.ts} +8 -7
  580. package/dist/types/generic-argument.d.ts.map +1 -0
  581. package/dist/types/generic-metadata-resolver.d.ts +8 -0
  582. package/dist/types/generic-metadata-resolver.d.ts.map +1 -0
  583. package/{generic-metadata.d.ts → dist/types/generic-metadata.d.ts} +8 -7
  584. package/dist/types/generic-metadata.d.ts.map +1 -0
  585. package/dist/types/generic-structure.d.ts +8 -0
  586. package/dist/types/generic-structure.d.ts.map +1 -0
  587. package/dist/types/index.d.ts +71 -0
  588. package/dist/types/index.d.ts.map +1 -0
  589. package/{inject-index.d.ts → dist/types/inject-index.d.ts} +7 -6
  590. package/dist/types/inject-index.d.ts.map +1 -0
  591. package/dist/types/inject-internals.d.ts +15 -0
  592. package/dist/types/inject-internals.d.ts.map +1 -0
  593. package/{inject-metadata.d.ts → dist/types/inject-metadata.d.ts} +118 -88
  594. package/dist/types/inject-metadata.d.ts.map +1 -0
  595. package/{inject-options.d.ts → dist/types/inject-options.d.ts} +23 -22
  596. package/dist/types/inject-options.d.ts.map +1 -0
  597. package/dist/types/inject-sorter.d.ts +20 -0
  598. package/dist/types/inject-sorter.d.ts.map +1 -0
  599. package/dist/types/inject-sorters/asc-inject-sorter.d.ts +21 -0
  600. package/dist/types/inject-sorters/asc-inject-sorter.d.ts.map +1 -0
  601. package/dist/types/inject-sorters/desc-inject-sorter.d.ts +21 -0
  602. package/dist/types/inject-sorters/desc-inject-sorter.d.ts.map +1 -0
  603. package/dist/types/inject-sorters/index.d.ts +3 -0
  604. package/dist/types/inject-sorters/index.d.ts.map +1 -0
  605. package/{inject.d.ts → dist/types/inject.d.ts} +11 -10
  606. package/dist/types/inject.d.ts.map +1 -0
  607. package/{injector.d.ts → dist/types/injector.d.ts} +17 -16
  608. package/dist/types/injector.d.ts.map +1 -0
  609. package/dist/types/injectors/index.d.ts +2 -0
  610. package/dist/types/injectors/index.d.ts.map +1 -0
  611. package/{injectors → dist/types/injectors}/singleton-injector.d.ts +32 -31
  612. package/dist/types/injectors/singleton-injector.d.ts.map +1 -0
  613. package/{log-level.d.ts → dist/types/log-level.d.ts} +12 -11
  614. package/dist/types/log-level.d.ts.map +1 -0
  615. package/{log.d.ts → dist/types/log.d.ts} +96 -95
  616. package/dist/types/log.d.ts.map +1 -0
  617. package/dist/types/metadata.d.ts +100 -0
  618. package/dist/types/metadata.d.ts.map +1 -0
  619. package/{naming-convention.d.ts → dist/types/naming-convention.d.ts} +16 -15
  620. package/dist/types/naming-convention.d.ts.map +1 -0
  621. package/{naming-conventions → dist/types/naming-conventions}/camel-case-naming-convention.d.ts +27 -26
  622. package/dist/types/naming-conventions/camel-case-naming-convention.d.ts.map +1 -0
  623. package/{naming-conventions → dist/types/naming-conventions}/flat-case-naming-convention.d.ts +26 -25
  624. package/dist/types/naming-conventions/flat-case-naming-convention.d.ts.map +1 -0
  625. package/{naming-conventions → dist/types/naming-conventions}/flat-upper-case-naming-convention.d.ts +26 -25
  626. package/dist/types/naming-conventions/flat-upper-case-naming-convention.d.ts.map +1 -0
  627. package/dist/types/naming-conventions/index.d.ts +9 -0
  628. package/dist/types/naming-conventions/index.d.ts.map +1 -0
  629. package/{naming-conventions → dist/types/naming-conventions}/kebab-case-naming-convention.d.ts +27 -26
  630. package/dist/types/naming-conventions/kebab-case-naming-convention.d.ts.map +1 -0
  631. package/{naming-conventions → dist/types/naming-conventions}/kebab-upper-case-naming-convention.d.ts +27 -26
  632. package/dist/types/naming-conventions/kebab-upper-case-naming-convention.d.ts.map +1 -0
  633. package/{naming-conventions → dist/types/naming-conventions}/pascal-case-naming-convention.d.ts +27 -26
  634. package/dist/types/naming-conventions/pascal-case-naming-convention.d.ts.map +1 -0
  635. package/{naming-conventions → dist/types/naming-conventions}/snake-case-naming-convention.d.ts +27 -26
  636. package/dist/types/naming-conventions/snake-case-naming-convention.d.ts.map +1 -0
  637. package/{naming-conventions → dist/types/naming-conventions}/snake-upper-case-naming-convention.d.ts +27 -26
  638. package/dist/types/naming-conventions/snake-upper-case-naming-convention.d.ts.map +1 -0
  639. package/dist/types/property-internals.d.ts +36 -0
  640. package/dist/types/property-internals.d.ts.map +1 -0
  641. package/{property-metadata.d.ts → dist/types/property-metadata.d.ts} +360 -309
  642. package/dist/types/property-metadata.d.ts.map +1 -0
  643. package/{property-name.d.ts → dist/types/property-name.d.ts} +7 -6
  644. package/dist/types/property-name.d.ts.map +1 -0
  645. package/{property-options.d.ts → dist/types/property-options.d.ts} +117 -116
  646. package/dist/types/property-options.d.ts.map +1 -0
  647. package/dist/types/property-sorter.d.ts +20 -0
  648. package/dist/types/property-sorter.d.ts.map +1 -0
  649. package/dist/types/property-sorters/asc-property-sorter.d.ts +21 -0
  650. package/dist/types/property-sorters/asc-property-sorter.d.ts.map +1 -0
  651. package/dist/types/property-sorters/desc-property-sorter.d.ts +21 -0
  652. package/dist/types/property-sorters/desc-property-sorter.d.ts.map +1 -0
  653. package/dist/types/property-sorters/index.d.ts +3 -0
  654. package/dist/types/property-sorters/index.d.ts.map +1 -0
  655. package/{property.d.ts → dist/types/property.d.ts} +14 -13
  656. package/dist/types/property.d.ts.map +1 -0
  657. package/{reference-callback.d.ts → dist/types/reference-callback.d.ts} +8 -7
  658. package/dist/types/reference-callback.d.ts.map +1 -0
  659. package/{reference-handler.d.ts → dist/types/reference-handler.d.ts} +32 -31
  660. package/dist/types/reference-handler.d.ts.map +1 -0
  661. package/{reference-handlers/direct-reference-handler.d.ts → dist/types/reference-handlers/circular-reference-handler.d.ts} +33 -32
  662. package/dist/types/reference-handlers/circular-reference-handler.d.ts.map +1 -0
  663. package/dist/types/reference-handlers/index.d.ts +4 -0
  664. package/dist/types/reference-handlers/index.d.ts.map +1 -0
  665. package/{reference-handlers/path-reference-handler.d.ts → dist/types/reference-handlers/json-path-reference-handler.d.ts} +56 -55
  666. package/dist/types/reference-handlers/json-path-reference-handler.d.ts.map +1 -0
  667. package/{reference-handlers/lead-reference-handler.d.ts → dist/types/reference-handlers/plain-reference-handler.d.ts} +34 -33
  668. package/dist/types/reference-handlers/plain-reference-handler.d.ts.map +1 -0
  669. package/{reference-key.d.ts → dist/types/reference-key.d.ts} +7 -6
  670. package/dist/types/reference-key.d.ts.map +1 -0
  671. package/{reference-value-getter.d.ts → dist/types/reference-value-getter.d.ts} +8 -7
  672. package/dist/types/reference-value-getter.d.ts.map +1 -0
  673. package/{reference-value-setter.d.ts → dist/types/reference-value-setter.d.ts} +8 -7
  674. package/dist/types/reference-value-setter.d.ts.map +1 -0
  675. package/{reference-value.d.ts → dist/types/reference-value.d.ts} +7 -6
  676. package/dist/types/reference-value.d.ts.map +1 -0
  677. package/{serializer-context-options.d.ts → dist/types/serializer-context-options.d.ts} +51 -43
  678. package/dist/types/serializer-context-options.d.ts.map +1 -0
  679. package/{serializer-context.d.ts → dist/types/serializer-context.d.ts} +419 -410
  680. package/dist/types/serializer-context.d.ts.map +1 -0
  681. package/{serializer.d.ts → dist/types/serializer.d.ts} +28 -27
  682. package/dist/types/serializer.d.ts.map +1 -0
  683. package/dist/types/serializers/any-serializer.d.ts +29 -0
  684. package/dist/types/serializers/any-serializer.d.ts.map +1 -0
  685. package/{serializers → dist/types/serializers}/array-buffer-serializer.d.ts +29 -28
  686. package/dist/types/serializers/array-buffer-serializer.d.ts.map +1 -0
  687. package/{serializers → dist/types/serializers}/array-serializer.d.ts +29 -28
  688. package/dist/types/serializers/array-serializer.d.ts.map +1 -0
  689. package/{serializers → dist/types/serializers}/boolean-serializer.d.ts +38 -37
  690. package/dist/types/serializers/boolean-serializer.d.ts.map +1 -0
  691. package/{serializers → dist/types/serializers}/data-view-serializer.d.ts +29 -28
  692. package/dist/types/serializers/data-view-serializer.d.ts.map +1 -0
  693. package/{serializers → dist/types/serializers}/date-serializer.d.ts +29 -28
  694. package/dist/types/serializers/date-serializer.d.ts.map +1 -0
  695. package/{serializers → dist/types/serializers}/float-32-array-serializer.d.ts +29 -28
  696. package/dist/types/serializers/float-32-array-serializer.d.ts.map +1 -0
  697. package/{serializers → dist/types/serializers}/float-64-array-serializer.d.ts +29 -28
  698. package/dist/types/serializers/float-64-array-serializer.d.ts.map +1 -0
  699. package/dist/types/serializers/index.d.ts +22 -0
  700. package/dist/types/serializers/index.d.ts.map +1 -0
  701. package/{serializers → dist/types/serializers}/int-16-array-serializer.d.ts +29 -28
  702. package/dist/types/serializers/int-16-array-serializer.d.ts.map +1 -0
  703. package/{serializers → dist/types/serializers}/int-32-array-serializer.d.ts +29 -28
  704. package/dist/types/serializers/int-32-array-serializer.d.ts.map +1 -0
  705. package/{serializers → dist/types/serializers}/int-8-array-serializer.d.ts +29 -28
  706. package/dist/types/serializers/int-8-array-serializer.d.ts.map +1 -0
  707. package/{serializers → dist/types/serializers}/map-serializer.d.ts +29 -28
  708. package/dist/types/serializers/map-serializer.d.ts.map +1 -0
  709. package/{serializers → dist/types/serializers}/number-serializer.d.ts +38 -37
  710. package/dist/types/serializers/number-serializer.d.ts.map +1 -0
  711. package/{serializers → dist/types/serializers}/set-serializer.d.ts +29 -28
  712. package/dist/types/serializers/set-serializer.d.ts.map +1 -0
  713. package/{serializers → dist/types/serializers}/string-serializer.d.ts +38 -37
  714. package/dist/types/serializers/string-serializer.d.ts.map +1 -0
  715. package/{serializers → dist/types/serializers}/type-serializer.d.ts +29 -28
  716. package/dist/types/serializers/type-serializer.d.ts.map +1 -0
  717. package/{serializers → dist/types/serializers}/uint-16-array-serializer.d.ts +29 -28
  718. package/dist/types/serializers/uint-16-array-serializer.d.ts.map +1 -0
  719. package/{serializers → dist/types/serializers}/uint-32-array-serializer.d.ts +29 -28
  720. package/dist/types/serializers/uint-32-array-serializer.d.ts.map +1 -0
  721. package/{serializers → dist/types/serializers}/uint-8-array-serializer.d.ts +29 -28
  722. package/dist/types/serializers/uint-8-array-serializer.d.ts.map +1 -0
  723. package/{serializers → dist/types/serializers}/uint-8-clamped-array-serializer.d.ts +29 -28
  724. package/dist/types/serializers/uint-8-clamped-array-serializer.d.ts.map +1 -0
  725. package/dist/types/serializers/unknown-serializer.d.ts +29 -0
  726. package/dist/types/serializers/unknown-serializer.d.ts.map +1 -0
  727. package/dist/types/type-abstraction.d.ts +7 -0
  728. package/dist/types/type-abstraction.d.ts.map +1 -0
  729. package/{type-argument.d.ts → dist/types/type-argument.d.ts} +10 -9
  730. package/dist/types/type-argument.d.ts.map +1 -0
  731. package/dist/types/type-configuration.d.ts +17 -0
  732. package/dist/types/type-configuration.d.ts.map +1 -0
  733. package/{type-context-entry.d.ts → dist/types/type-context-entry.d.ts} +37 -36
  734. package/dist/types/type-context-entry.d.ts.map +1 -0
  735. package/{type-context.d.ts → dist/types/type-context.d.ts} +71 -58
  736. package/dist/types/type-context.d.ts.map +1 -0
  737. package/{type-ctor.d.ts → dist/types/type-ctor.d.ts} +7 -6
  738. package/dist/types/type-ctor.d.ts.map +1 -0
  739. package/{type-fn.d.ts → dist/types/type-fn.d.ts} +9 -8
  740. package/dist/types/type-fn.d.ts.map +1 -0
  741. package/dist/types/type-internals.d.ts +15 -0
  742. package/dist/types/type-internals.d.ts.map +1 -0
  743. package/{type-like.d.ts → dist/types/type-like.d.ts} +8 -7
  744. package/dist/types/type-like.d.ts.map +1 -0
  745. package/{type-manager-options.d.ts → dist/types/type-manager-options.d.ts} +34 -24
  746. package/dist/types/type-manager-options.d.ts.map +1 -0
  747. package/dist/types/type-manager.d.ts +414 -0
  748. package/dist/types/type-manager.d.ts.map +1 -0
  749. package/{type-metadata-resolver.d.ts → dist/types/type-metadata-resolver.d.ts} +9 -8
  750. package/dist/types/type-metadata-resolver.d.ts.map +1 -0
  751. package/{type-metadata-symbol.d.ts → dist/types/type-metadata-symbol.d.ts} +7 -6
  752. package/dist/types/type-metadata-symbol.d.ts.map +1 -0
  753. package/{type-metadata.d.ts → dist/types/type-metadata.d.ts} +514 -472
  754. package/dist/types/type-metadata.d.ts.map +1 -0
  755. package/{type-name.d.ts → dist/types/type-name.d.ts} +7 -6
  756. package/dist/types/type-name.d.ts.map +1 -0
  757. package/{type-options-base.d.ts → dist/types/type-options-base.d.ts} +107 -90
  758. package/dist/types/type-options-base.d.ts.map +1 -0
  759. package/{type-options.d.ts → dist/types/type-options.d.ts} +73 -79
  760. package/dist/types/type-options.d.ts.map +1 -0
  761. package/{type-resolver.d.ts → dist/types/type-resolver.d.ts} +8 -7
  762. package/dist/types/type-resolver.d.ts.map +1 -0
  763. package/{type.d.ts → dist/types/type.d.ts} +10 -9
  764. package/dist/types/type.d.ts.map +1 -0
  765. package/dist/types/unknown.d.ts +10 -0
  766. package/dist/types/unknown.d.ts.map +1 -0
  767. package/dist/umd/index.js +2 -0
  768. package/dist/umd/index.js.map +1 -0
  769. package/package.json +63 -55
  770. package/custom-data.d.ts +0 -6
  771. package/factories/index.d.ts +0 -1
  772. package/index.js +0 -1
  773. package/metadata.d.ts +0 -42
  774. package/reference-handlers/index.d.ts +0 -3
  775. package/type-abstraction.d.ts +0 -8
  776. package/type-manager.d.ts +0 -331
package/README.md CHANGED
@@ -1,1857 +1,1901 @@
1
- # TypeManager.TS
2
-
3
- ![GitHub](https://img.shields.io/github/license/dipscope/TypeManager.TS) ![NPM](https://img.shields.io/npm/v/@dipscope/type-manager) ![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)
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.
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!
12
-
13
- ## Table of contents
14
-
15
- * [What issues it solves?](#what-issues-it-solves)
16
- * [Installation](#installation)
17
- * [How it works?](#how-it-works)
18
- * [Defining decorators](#defining-decorators)
19
- * [Type decorator](#type-decorator)
20
- * [Property decorator](#property-decorator)
21
- * [Inject decorator](#inject-decorator)
22
- * [Defining decorator options](#defining-decorator-options)
23
- * [Alias option](#alias-option)
24
- * [Custom data option](#custom-data-option)
25
- * [Default value option](#default-value-option)
26
- * [Deserializable option](#deserializable-option)
27
- * [Discriminant option](#discriminant-option)
28
- * [Discriminator option](#discriminator-option)
29
- * [Factory option](#factory-option)
30
- * [Injectable option](#injectable-option)
31
- * [Injector option](#injector-option)
32
- * [Naming convention option](#naming-convention-option)
33
- * [Preserve discriminator option](#preserve-discriminator-option)
34
- * [Preserve null option](#preserve-null-option)
35
- * [Reference handler option](#reference-handler-option)
36
- * [Serializable option](#serializable-option)
37
- * [Serializer option](#serializer-option)
38
- * [Use default value option](#use-default-value-option)
39
- * [Use implicit conversion option](#use-implicit-conversion-option)
40
- * [Defining configuration manually](#defining-configuration-manually)
41
- * [Configuring global options](#configuring-global-options)
42
- * [Configuring options per type](#configuring-options-per-type)
43
- * [Configuring usage of polymorphic types](#configuring-usage-of-polymorphic-types)
44
- * [Configuring naming convention](#configuring-naming-convention)
45
- * [Configuring reference handler](#configuring-reference-handler)
46
- * [Advanced usage](#advanced-usage)
47
- * [Defining custom data](#defining-custom-data)
48
- * [Defining custom serializer](#defining-custom-serializer)
49
- * [Defining custom injector](#defining-custom-injector)
50
- * [Defining custom factory](#defining-custom-factory)
51
- * [Defining custom naming convention](#defining-custom-naming-convention)
52
- * [Use cases](#use-cases)
53
- * [Built in serializers](#built-in-serializers)
54
- * [Circular object references](#circular-object-references)
55
- * [Dependency injection and immutable types](#dependency-injection-and-immutable-types)
56
- * [Different case usage in class and JSON](#different-case-usage-in-class-and-json)
57
- * [Enum types](#enum-types)
58
- * [Generic types](#generic-types)
59
- * [Integration with Angular](#integration-with-angular)
60
- * [Polymorphic types](#polymorphic-types)
61
- * [Versioning](#versioning)
62
- * [Contributing](#contributing)
63
- * [Authors](#authors)
64
- * [Notes](#notes)
65
- * [License](#license)
66
-
67
- ## What issues it solves?
68
-
69
- In `JavaScript` there are two types of objects:
70
-
71
- * Plain objects which are created using `{}` notation;
72
- * Constructor based objects which are created using `new Class()` notation.
73
-
74
- Sometimes we want to transform plain objects to the classes we have. Let's assume we are loading a users JSON data from our backend API or the other datasource:
75
-
76
- ```json
77
- [
78
- {
79
- "id": 1,
80
- "name": "Dmitry",
81
- "deletedAt": "2021-02-22T20:15:00.000Z"
82
- },
83
- {
84
- "id": 2,
85
- "name": "Alex",
86
- "deletedAt": null
87
- },
88
- {
89
- "id": 3,
90
- "name": "Anna",
91
- "deletedAt": null
92
- }
93
- ]
94
- ```
95
-
96
- We have a `User` class:
97
-
98
- ```typescript
99
- export class User
100
- {
101
- public id: number;
102
- public name: string;
103
- public deletedAt: Date;
104
-
105
- public constructor(id: number, name: string)
106
- {
107
- this.id = id;
108
- this.name = name;
109
-
110
- return;
111
- }
112
-
113
- public isDeleted(): boolean
114
- {
115
- return this.deletedAt !== null;
116
- }
117
- }
118
- ```
119
-
120
- Somewhere in code we are checking if user is deleted before performing some action.
121
-
122
- ```typescript
123
- const users: User[] = JSON.parse(usersJson);
124
-
125
- for (const user of users)
126
- {
127
- if (user.isDeleted())
128
- {
129
- // Do some action...
130
- }
131
- }
132
- ```
133
-
134
- Do you see the problem in this piece of code?
135
-
136
- `JSON.parse` function returns plain objects and we actually lied to compiler when said that it is a `User` class array. In this code we can successfully use `user.id` or `user.name`. However we cannot use `user.isDeleted()` because user is not an instance of a `User` class.
137
-
138
- So what to do? How to get `User` array from our JSON?
139
-
140
- 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.
141
-
142
- There exists much more simple way. Let's use our `TypeManager` for getting instances of `User` class from JSON:
143
-
144
- ```typescript
145
- import { TypeManager } from '@dipscope/type-manager';
146
-
147
- const users: User[] = TypeManager.parse(User, usersJson);
148
-
149
- for (const user of users)
150
- {
151
- if (user.isDeleted())
152
- {
153
- // Do some action...
154
- }
155
- }
156
- ```
157
-
158
- Now we can use all power provided by `JavaScript` class instances.
159
-
160
- Furthermore `TypeManager.TS` provides you:
161
-
162
- * Reflection abilities at runtime;
163
- * Support for generic types;
164
- * Handling of cyclic object references and different ways of serialization when they appear;
165
- * Ability to configure serialization of 3rd party classes;
166
- * Support for polymorphic types;
167
- * Great alternative for similar packages like [class-transformer](https://github.com/typestack/class-transformer), [TypedJSON](https://github.com/JohnWeisz/TypedJSON) and [jackson-js](https://github.com/pichillilorenzo/jackson-js);
168
-
169
- Want to know more? Let's dive into the details.
170
-
171
- ## Installation
172
-
173
- `TypeManager.TS` is available from NPM, both for browser (e.g. using webpack) and NodeJS:
174
-
175
- ```
176
- npm i @dipscope/type-manager
177
- ```
178
-
179
- TypeScript needs to run with the `experimentalDecorators` and `emitDecoratorMetadata` options enabled when using decorator annotations. So make sure you have properly configured your `tsconfig.json` file.
180
-
181
- _If you want additional type-safety and reduced syntax you may wish to install [reflect-metadata](https://github.com/rbuckton/reflect-metadata). This step is on your choice and fully optional. When installed it must be available globally to work. This can usually be done with `import 'reflect-metadata';` in your main index file._
182
-
183
- ## How it works?
184
-
185
- 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:
186
-
187
- * Using decorator annotations;
188
- * Using declarative style;
189
-
190
- 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.
191
-
192
- Let's have a look at the most simple example of configuration using decorators.
193
-
194
- ```typescript
195
- import { Type, Property } from '@dipscope/type-manager';
196
-
197
- @Type()
198
- export class User
199
- {
200
- @Property(String) public name: string;
201
- @Property(String) public email: string;
202
- }
203
- ```
204
-
205
- 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.
206
-
207
- The same configuration can be rewritten using declarative style.
208
-
209
- ```typescript
210
- import { TypeManager, PropertyName, PropertyOptions } from '@dipscope/type-manager';
211
-
212
- export class User
213
- {
214
- public name: string;
215
- public email: string;
216
- }
217
-
218
- TypeManager.configureTypeOptions(User, {
219
- propertyOptionsMap: new Map<PropertyName, PropertyOptions<any>>([
220
- ['name', { typeArgument: String }],
221
- ['email', { typeArgument: String }],
222
- ])
223
- });
224
- ```
225
-
226
- 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.
227
-
228
- 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.
229
-
230
- ```typescript
231
- import { TypeManager } from '@dipscope/type-manager';
232
-
233
- const userObject = TypeManager.serialize(User, new User());
234
- const user = TypeManager.deserialize(User, userObject);
235
-
236
- user instanceof User; // True.
237
- ```
238
-
239
- Calling serialize creates a plain object and deserialize creates an instance of `User` class. During deserialize you can provide any object. It's not nesassary that object was produced by `TypeManager`. If object is an `Array` you will get array of types in return. Objects are parsed based on general type configuration defined by developer. It is also possible to stringify and parse JSON.
240
-
241
- ```typescript
242
- import { TypeManager } from '@dipscope/type-manager';
243
-
244
- const userJson = TypeManager.stringify(User, new User());
245
- const user = TypeManager.parse(User, userJson);
246
-
247
- user instanceof User; // True.
248
- ```
249
-
250
- Stringify and parse functions are wrappers over native JSON class functions. In addition they add serialize and deserialize support under the hood.
251
-
252
- Static functions are not the only way to work with a `TypeManager`. You can also work on instance based manner.
253
-
254
- ```typescript
255
- import { TypeManager } from '@dipscope/type-manager';
256
-
257
- const userManager = new TypeManager(User);
258
- const userObject = userManager.serialize(new User());
259
- const user = userManager.deserialize(userObject);
260
-
261
- user instanceof User; // True.
262
- ```
263
-
264
- 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.
265
-
266
- ## Defining decorators
267
-
268
- There are few decorators which controls the main flow. This are `Type`, `Property` and `Inject` decorators. Let's go through each of them.
269
-
270
- ### Type decorator
271
-
272
- Type decorator defines a type and should be declared right before a class.
273
-
274
- ```typescript
275
- import { Type } from '@dipscope/type-manager';
276
-
277
- @Type()
278
- export class User
279
- {
280
- ...
281
- }
282
- ```
283
-
284
- This will register a new type with default type serializer assigned to it. You can define how each class should be treated by providing optional configure options as a first argument.
285
-
286
- ```typescript
287
- import { Type } from '@dipscope/type-manager';
288
-
289
- @Type({
290
- alias: 'User',
291
- serializer: new UserSerializer()
292
- })
293
- export class User
294
- {
295
- ...
296
- }
297
- ```
298
-
299
- 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.
300
-
301
- There are more options can be provided for a type, so check `TypeOptions` definition or section with [decorator options](#defining-decorator-options) below.
302
-
303
- ### Property decorator
304
-
305
- Property decorator defines per property configuration within a type and should be declared right before a property or accessor definition.
306
-
307
- ```typescript
308
- import 'reflect-metadata';
309
- import { Type, Property } from '@dipscope/type-manager';
310
-
311
- @Type()
312
- export class User
313
- {
314
- @Property() public name: string;
315
- }
316
- ```
317
-
318
- 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.
319
-
320
- ```typescript
321
- import { Type, Property } from '@dipscope/type-manager';
322
-
323
- @Type()
324
- export class User
325
- {
326
- @Property(String) public name: string;
327
- }
328
- ```
329
-
330
- Depending on your use case there are two possible ways to configure additional property options.
331
-
332
- If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) then provide options as a first argument.
333
-
334
- ```typescript
335
- import 'reflect-metadata';
336
- import { Type, Property } from '@dipscope/type-manager';
337
-
338
- @Type()
339
- export class User
340
- {
341
- @Property({ alias: 'username' }) public name: string;
342
- }
343
- ```
344
-
345
- If types defined explicitly then provide options as a second argument.
346
-
347
- ```typescript
348
- import { Type, Property } from '@dipscope/type-manager';
349
-
350
- @Type()
351
- export class User
352
- {
353
- @Property(String, { alias: 'username' }) public name: string;
354
- }
355
- ```
356
-
357
- 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 [decorator options](#defining-decorator-options) below. For example you can make some properties serializable only or define custom property serialization.
358
-
359
- 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.
360
-
361
- 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.
362
-
363
- ```typescript
364
- import 'reflect-metadata';
365
- import { Type, Property } from '@dipscope/type-manager';
366
-
367
- @Type()
368
- export class User
369
- {
370
- @Property([String, Number], { alias: 'myMap' }) public map: Map<string, number>;
371
- }
372
- ```
373
-
374
- If types defined explicitly then provide generics as a second argument so configure options will become the third.
375
-
376
- ```typescript
377
- import { Type, Property } from '@dipscope/type-manager';
378
-
379
- @Type()
380
- export class User
381
- {
382
- @Property(Map, [String, Number], { alias: 'myMap' }) public map: Map<string, number>;
383
- }
384
- ```
385
-
386
- 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.
387
-
388
- 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.
389
-
390
- ```typescript
391
- import { Type, Property } from '@dipscope/type-manager';
392
-
393
- @Type()
394
- export class User
395
- {
396
- @Property({
397
- typeArgument: Map,
398
- genericArguments: [String, Number],
399
- alias: 'myMap'
400
- })
401
- public map: Map<string, number>;
402
- }
403
- ```
404
-
405
- Which syntax to use is completely on your choice. `Property` decorator is smart enough to setup everything based on usage.
406
-
407
- 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.
408
-
409
- ```typescript
410
- import 'reflect-metadata';
411
- import { Type, Property } from '@dipscope/type-manager';
412
-
413
- @Type()
414
- export class UserStatus
415
- {
416
- @Property() public title: string;
417
- }
418
-
419
- @Type()
420
- export class User
421
- {
422
- @Property() public userStatus: UserStatus;
423
- }
424
- ```
425
-
426
- With array of relations you have to use generics.
427
-
428
- ```typescript
429
- import 'reflect-metadata';
430
- import { Type, Property } from '@dipscope/type-manager';
431
-
432
- @Type()
433
- export class UserStatus
434
- {
435
- @Property() public title: string;
436
- }
437
-
438
- @Type()
439
- export class User
440
- {
441
- @Property([UserStatus]) public userStatuses: UserStatus[];
442
- }
443
- ```
444
-
445
- If types defined explicitly then definition will be the following.
446
-
447
- ```typescript
448
- import { Type, Property } from '@dipscope/type-manager';
449
-
450
- @Type()
451
- export class UserStatus
452
- {
453
- @Property(String) public title: string;
454
- }
455
-
456
- @Type()
457
- export class User
458
- {
459
- @Property(UserStatus) public userStatus: UserStatus;
460
- }
461
- ```
462
-
463
- Then for array of relations it will be the following.
464
-
465
- ```typescript
466
- import { Type, Property } from '@dipscope/type-manager';
467
-
468
- @Type()
469
- export class UserStatus
470
- {
471
- @Property(String) public title: string;
472
- }
473
-
474
- @Type()
475
- export class User
476
- {
477
- @Property(Array, [UserStatus]) public userStatuses: UserStatus[];
478
- }
479
- ```
480
-
481
- If any type defines an alias - it can be used as a type resolver.
482
-
483
- ```typescript
484
- import { Type, Property } from '@dipscope/type-manager';
485
-
486
- @Type({
487
- alias: 'UserStatus'
488
- })
489
- export class UserStatus
490
- {
491
- @Property(String) public title: string;
492
- }
493
-
494
- @Type()
495
- export class User
496
- {
497
- @Property('UserStatus') public userStatus: UserStatus;
498
- }
499
- ```
500
-
501
- If you have circular reference or your type declared later an extended syntax can be used to lazily define a type.
502
-
503
- ```typescript
504
- import { Type, Property } from '@dipscope/type-manager';
505
-
506
- @Type()
507
- export class User
508
- {
509
- @Property(() => UserStatus) public userStatus: UserStatus;
510
- }
511
-
512
- @Type()
513
- export class UserStatus
514
- {
515
- @Property(() => String) public title: string;
516
- }
517
- ```
518
-
519
- 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.
520
-
521
- 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).
522
-
523
- 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.
524
-
525
- ```typescript
526
- import 'reflect-metadata';
527
- import { Type, Property } from '@dipscope/type-manager';
528
-
529
- export enum UserPriorityNumeric
530
- {
531
- Low,
532
- Medium,
533
- High
534
- }
535
-
536
- export enum UserPriorityTextual
537
- {
538
- Low = 'Low',
539
- Medium = 'Medium',
540
- High = 'High'
541
- }
542
-
543
- @Type()
544
- export class User
545
- {
546
- @Property() public userPriorityNumeric: UserPriorityNumeric;
547
- @Property() public userPriorityTextual: UserPriorityTextual;
548
- }
549
- ```
550
-
551
- If types defined explicitly then definition will be the following.
552
-
553
- ```typescript
554
- import { Type, Property } from '@dipscope/type-manager';
555
-
556
- export enum UserPriorityNumeric
557
- {
558
- Low,
559
- Medium,
560
- High
561
- }
562
-
563
- export enum UserPriorityTextual
564
- {
565
- Low = 'Low',
566
- Medium = 'Medium',
567
- High = 'High'
568
- }
569
-
570
- @Type()
571
- export class User
572
- {
573
- @Property(Number) public userPriorityNumeric: UserPriorityNumeric;
574
- @Property(String) public userPriorityTextual: UserPriorityTextual;
575
- }
576
- ```
577
-
578
- One should remember this when explicitly defining types for enums.
579
-
580
- ### Inject decorator
581
-
582
- Inject decorator controls your type dependency and declared right before a constructor parameter.
583
-
584
- ```typescript
585
- import { Type, Inject } from '@dipscope/type-manager';
586
-
587
- @Type()
588
- export class User
589
- {
590
- public constructor(@Inject('name') name: string, @Inject(UserService) userService: UserService)
591
- {
592
- this.name = name;
593
-
594
- // Any action with UserService...
595
-
596
- return;
597
- }
598
- }
599
- ```
600
-
601
- It accepts two possible inputs as its argument.
602
-
603
- * String key from JSON context;
604
- * Certain type registered as injectable;
605
-
606
- When a string key is provided then a certain value will be resolved from JSON context for you when creating an instance. If any property declares the same key you will recieve deserialized value. If not then original value will be provided instead. Injecting context values is a use case of immutable types.
607
-
608
- When a certain type is provided it will be resolved from the dependency injection container. If you are going to use internal type injector then you should register injectable types as the following. By default singleton injector is used to resolve such services.
609
-
610
- ```typescript
611
- import { Type } from '@dipscope/type-manager';
612
-
613
- @Type({
614
- injectable: true
615
- })
616
- export class UserService
617
- {
618
- public property: string;
619
- }
620
- ```
621
-
622
- 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. You can read more about [creating a custom injector](#defining-custom-injector) in a separate section.
623
-
624
- If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) the injection of services can be simplified.
625
-
626
- ```typescript
627
- import { Type, Inject } from '@dipscope/type-manager';
628
-
629
- @Type()
630
- export class User
631
- {
632
- public constructor(@Inject('name') name: string, userService: UserService)
633
- {
634
- this.name = name;
635
-
636
- // Any action with UserService...
637
-
638
- return;
639
- }
640
- }
641
- ```
642
-
643
- Note that now you don't have to specify injection for types explicitly. However injection of values by key from JSON context still present. It's because argument names cannot be resolved using reflection.
644
-
645
- ## Defining decorator options
646
-
647
- `Type` and `Property` decorators provide full configuration for your classes using configure options. In this section we will go through each of them.
648
-
649
- ### Alias option
650
-
651
- This option can be used both on type and property to define an alias.
652
-
653
- ```typescript
654
- import { Type, Property } from '@dipscope/type-manager';
655
-
656
- @Type({
657
- alias: 'User'
658
- })
659
- export class User
660
- {
661
- @Property(String, { alias: 'username' }) public name: string;
662
- }
663
- ```
664
-
665
- Alias defined for a class can be used later for resolving property types. Note that it should be unique within application to work properly.
666
-
667
- Alias defined for a property declares that property name differs from one specified in JSON. In our case `username` will be used instead of `name` during JSON serialization and deserialization.
668
-
669
- ### Custom data option
670
-
671
- This option can be used to provide any custom data for type or property.
672
-
673
- ```typescript
674
- import { Type, Property } from '@dipscope/type-manager';
675
-
676
- @Type({
677
- customData: { rank: 1 }
678
- })
679
- export class User
680
- {
681
- @Property(String, { customData: { order: 1 } }) public name: string;
682
- }
683
- ```
684
-
685
- 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.
686
-
687
- ### Default value option
688
-
689
- This option is used to define a default value when one is undefined. It can be used on type or property.
690
-
691
- ```typescript
692
- import { Type, Property } from '@dipscope/type-manager';
693
-
694
- @Type({
695
- defaultValue: () => new User()
696
- })
697
- export class User
698
- {
699
- @Property(String, { defaultValue: 'BestName' }) public name: string;
700
- }
701
- ```
702
-
703
- This will affect both serialized and deserialized default value. This will fit perfectly for most types. You can also specify serialized and deserialized default value explicitly for complex types by using two other options.
704
-
705
- ```typescript
706
- import { Type, Property } from '@dipscope/type-manager';
707
-
708
- @Type({
709
- serializedDefaultValue: () => new User(),
710
- deserializedDefaultValue: () => new User()
711
- })
712
- export class User
713
- {
714
- @Property(String, { serializedDefaultValue: 'SerializedName', deserializedDefaultValue: 'DeserializedName' }) public name: string;
715
- }
716
- ```
717
-
718
- 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` option per type and property or enable globally using `TypeManager` configure method.
719
-
720
- ### Deserializable option
721
-
722
- This option is used to enable or disable deserialization for a certain property.
723
-
724
- ```typescript
725
- import { Type, Property } from '@dipscope/type-manager';
726
-
727
- @Type()
728
- export class User
729
- {
730
- @Property(String, { deserializable: true }) public name: string;
731
- }
732
- ```
733
-
734
- By default all properties are deserializable.
735
-
736
- ### Discriminant option
737
-
738
- This option is used to define a custom discriminant for a type which is later used during serialization and deserialization of polymorphic types.
739
-
740
- ```typescript
741
- import { Type, Property } from '@dipscope/type-manager';
742
-
743
- @Type({
744
- discriminant: 'Company.Api.Entities.User'
745
- })
746
- export class User
747
- {
748
- @Property(String) public name: string;
749
- }
750
- ```
751
-
752
- You can read more about handling of polymorphic types in this [section](#configuring-usage-of-polymorphic-types).
753
-
754
- ### Discriminator option
755
-
756
- This option can be used to define a custom property which stores discriminant of polymorphic type.
757
-
758
- ```typescript
759
- import { Type, Property } from '@dipscope/type-manager';
760
-
761
- @Type({
762
- discriminator: '__typename__'
763
- })
764
- export class User
765
- {
766
- @Property(String) public name: string;
767
- }
768
- ```
769
-
770
- 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).
771
-
772
- ### Factory option
773
-
774
- This option can be used to register a handler which should be used for constructing a type instead of default one.
775
-
776
- ```typescript
777
- import { Type, Property } from '@dipscope/type-manager';
778
-
779
- @Type({
780
- factory: new UserFactory()
781
- })
782
- export class User
783
- {
784
- @Property(String) public name: string;
785
- }
786
- ```
787
-
788
- 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.
789
-
790
- ### Injectable option
791
-
792
- This option is used to register a type in dependency injection container.
793
-
794
- ```typescript
795
- import { Type } from '@dipscope/type-manager';
796
-
797
- @Type({
798
- injectable: true
799
- })
800
- export class UserService
801
- {
802
- public property: string;
803
- }
804
- ```
805
-
806
- Injectable type later can be provided as a dependency.
807
-
808
- ```typescript
809
- import { Type, Property, Inject } from '@dipscope/type-manager';
810
-
811
- @Type()
812
- export class User
813
- {
814
- @Property(String) public name: string;
815
-
816
- public constructor(@Inject(UserService) userService: UserService)
817
- {
818
- // Any action with UserService...
819
-
820
- return;
821
- }
822
- }
823
- ```
824
-
825
- 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. You can read more about [creating a custom injector](#defining-custom-injector) in a separate section.
826
-
827
- ### Injector option
828
-
829
- This option can be used to define a custom injector implementation which should be used in a type scope.
830
-
831
- ```typescript
832
- import { Type, Property } from '@dipscope/type-manager';
833
-
834
- @Type({
835
- injector: new UserInjector()
836
- })
837
- export class User
838
- {
839
- @Property(String) public name: string;
840
- }
841
- ```
842
-
843
- 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.
844
-
845
- ### Naming convention option
846
-
847
- This option can be used both on type and property to provide custom naming strategy.
848
-
849
- ```typescript
850
- import { Type, Property, CamelCaseNamingConvention, SnakeCaseNamingConvention } from '@dipscope/type-manager';
851
-
852
- @Type({
853
- namingConvention: new CamelCaseNamingConvention()
854
- })
855
- export class User
856
- {
857
- @Property(String, { namingConvention: new SnakeCaseNamingConvention() }) public name: string;
858
- }
859
- ```
860
-
861
- 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.
862
-
863
- ### Preserve discriminator option
864
-
865
- This option defines if discriminator should be preserved in objects during serialization and deserialization.
866
-
867
- ```typescript
868
- import { Type, Property } from '@dipscope/type-manager';
869
-
870
- @Type({
871
- preserveDiscriminator: true
872
- })
873
- export class User
874
- {
875
- @Property(String) public name: string;
876
- }
877
- ```
878
-
879
- 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).
880
-
881
- ### Preserve null option
882
-
883
- This option defines if null values should be preserved during serialization and deserialization.
884
-
885
- ```typescript
886
- import { Type, Property } from '@dipscope/type-manager';
887
-
888
- @Type({
889
- preserveNull: true
890
- })
891
- export class User
892
- {
893
- @Property(String, { preserveNull: false }) public name: string;
894
- }
895
- ```
896
-
897
- By default null values are preserved. You can set it to `false` per type, property or globally using `TypeManager` configure method. This will result in treating null values as undefined so you will get all related behaviours like setting default values.
898
-
899
- ### Reference handler option
900
-
901
- This option can be used both on type and property to specify how references to the same objects should be handled during serialization and deserialization.
902
-
903
- ```typescript
904
- import { Type, Property, DirectReferenceHandler, LeadReferenceHandler } from '@dipscope/type-manager';
905
-
906
- @Type({
907
- referenceHandler: new DirectReferenceHandler()
908
- })
909
- export class User
910
- {
911
- @Property(String, { referenceHandler: new LeadReferenceHandler() }) public name: string;
912
- }
913
- ```
914
-
915
- 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.
916
-
917
- ### Serializable option
918
-
919
- This option is used to enable or disable serialization for a certain property.
920
-
921
- ```typescript
922
- import { Type, Property } from '@dipscope/type-manager';
923
-
924
- @Type()
925
- export class User
926
- {
927
- @Property(String, { serializable: true }) public name: string;
928
- }
929
- ```
930
-
931
- By default all properties are serializable.
932
-
933
- ### Serializer option
934
-
935
- This option is used to define custom serializer for a type or property.
936
-
937
- ```typescript
938
- import { Type, Property } from '@dipscope/type-manager';
939
-
940
- @Type({
941
- serializer: new UserSerializer()
942
- })
943
- export class User
944
- {
945
- @Property(String, { serializer: new UserNameSerializer() }) public name: string;
946
- }
947
- ```
948
-
949
- 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.
950
-
951
- ### Use default value option
952
-
953
- This option enables or disables using default value per type or property.
954
-
955
- ```typescript
956
- import { Type, Property } from '@dipscope/type-manager';
957
-
958
- @Type({
959
- useDefaultValue: true
960
- })
961
- export class User
962
- {
963
- @Property(String, { useDefaultValue: false }) public name: string;
964
- }
965
- ```
966
-
967
- Using default values is turned off by default. You can enable them globally using `TypeManager` configure method.
968
-
969
- ### Use implicit conversion option
970
-
971
- 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.
972
-
973
- ```typescript
974
- import { Type, Property } from '@dipscope/type-manager';
975
-
976
- @Type()
977
- export class User
978
- {
979
- @Property(String, { useImplicitConversion: true }) public name: string;
980
- }
981
- ```
982
-
983
- 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` option per type and property or enable globally using `TypeManager` configure method.
984
-
985
- ## Defining configuration manually
986
-
987
- 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.
988
-
989
- 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.
990
-
991
- ### Configuring global options
992
-
993
- There are several options which can be configured globally. For example let's override using of default value option so we don't have to specify it explicitly per type or property.
994
-
995
- ```typescript
996
- import { TypeManagerOptions, TypeOptionsBase } from '@dipscope/type-manager';
997
-
998
- const typeOptionsBase: TypeOptionsBase<any> = {
999
- useDefaultValue: true
1000
- };
1001
-
1002
- const typeManagerOptions: TypeManagerOptions = {
1003
- typeOptionsBase: typeOptionsBase
1004
- };
1005
-
1006
- TypeManager.configure(typeManagerOptions);
1007
- ```
1008
-
1009
- For the full list of available global options check `TypeOptionsBase` definition or follow the documentation as we are going to touch them while we proceed.
1010
-
1011
- ### Configuring options per type
1012
-
1013
- Here is an example of declarative configuration which can be used for 3rd party or your own classes in addition to decorators.
1014
-
1015
- ```typescript
1016
- import { DateTime } from '@external-library';
1017
- import { TypeManagerOptions, TypeFn, TypeOptions, PropertyName, PropertyOptions } from '@dipscope/type-manager';
1018
-
1019
- const dateTimeOptions: TypeOptions<DateTime> = {
1020
- alias: 'DateTime',
1021
- serializer: new DateTimeSerializer()
1022
- };
1023
-
1024
- const userOptions: TypeOptions<User> = {
1025
- alias: 'User',
1026
- propertyOptionsMap: new Map<PropertyName, PropertyOptions<any>>(
1027
- ['name', { typeArgument: String, serializable: true, alias: 'username' }],
1028
- ['createdAt', { typeArgument: DateTime }]
1029
- )
1030
- };
1031
-
1032
- const typeManagerOptions: TypeManagerOptions = {
1033
- typeOptionsMap: new Map<TypeFn<any>, TypeOptions<any>>(
1034
- [DateTime, dateTimeOptions],
1035
- [User, userOptions]
1036
- )
1037
- };
1038
-
1039
- TypeManager.configure(typeManagerOptions);
1040
- ```
1041
-
1042
- There is a well defined order to how type options are applied when using configure methods on one type. One should remember this when combining and overriding options in different places.
1043
-
1044
- 1. Base type options are applied;
1045
- 2. Decorator type options are applied;
1046
- 3. Declarative type options are applied;
1047
- 4. Property type options are applied;
1048
-
1049
- Declarative configuration supports the same options as decorators. With such a configuration you can declare types like the following.
1050
-
1051
- ```typescript
1052
- export class User
1053
- {
1054
- public name: string;
1055
- public createdAt: DateTime;
1056
- }
1057
- ```
1058
-
1059
- Also if you are declaring only 3rd party classes the use case can be the following with [reflect-metadata](https://github.com/rbuckton/reflect-metadata).
1060
-
1061
- ```typescript
1062
- import 'reflect-metadata';
1063
- import { Type, Property } from '@dipscope/type-manager';
1064
-
1065
- @Type()
1066
- export class User
1067
- {
1068
- @Property() public name: string;
1069
- @Property() public createdAt: DateTime;
1070
- }
1071
- ```
1072
-
1073
- And without [reflect-metadata](https://github.com/rbuckton/reflect-metadata).
1074
-
1075
- ```typescript
1076
- import { Type, Property } from '@dipscope/type-manager';
1077
-
1078
- @Type()
1079
- export class User
1080
- {
1081
- @Property(String) public name: string;
1082
- @Property(DateTime) public createdAt: DateTime;
1083
- }
1084
- ```
1085
-
1086
- ### Configuring usage of polymorphic types
1087
-
1088
- 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.
1089
-
1090
- ```typescript
1091
- import { Type, Property } from '@dipscope/type-manager';
1092
-
1093
- @Type()
1094
- export abstract class Shape
1095
- {
1096
- @Property(String) public title: string;
1097
- }
1098
-
1099
- @Type()
1100
- export class Rectangle extends Shape
1101
- {
1102
- @Property(Number) public width: number;
1103
- @Property(Number) public height: number;
1104
- }
1105
-
1106
- @Type()
1107
- export class Square extends Shape
1108
- {
1109
- @Property(Number) public width: number;
1110
- }
1111
-
1112
- @Type()
1113
- export class Circle extends Shape
1114
- {
1115
- @Property(Number) public radius: number;
1116
- }
1117
- ```
1118
-
1119
- Some other class declares a `shapes` property in it.
1120
-
1121
- ```typescript
1122
- import { Type, Property } from '@dipscope/type-manager';
1123
-
1124
- @Type()
1125
- export class Plot
1126
- {
1127
- @Property(Array, [Shape]) public shapes: Shape[];
1128
- }
1129
- ```
1130
-
1131
- 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.
1132
-
1133
- ```json
1134
- {
1135
- "shapes": [
1136
- {
1137
- "title": "Cool rectangle",
1138
- "width": 10,
1139
- "height": 10
1140
- },
1141
- {
1142
- "title": "Perfect square",
1143
- "width": 10
1144
- },
1145
- {
1146
- "title": "Simple circle",
1147
- "radius": 6
1148
- }
1149
- ]
1150
- }
1151
- ```
1152
-
1153
- 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?
1154
-
1155
- 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.
1156
-
1157
- It uses 2 special configurable type options:
1158
-
1159
- * `Discriminator` which defines a property inside an object which should be used to define a type.
1160
- * `Discriminant` which represents a certain `Discriminator` value.
1161
-
1162
- This options have default values if you have not configured them explicitly.
1163
-
1164
- * Default value of discriminator is a `$type`. During deserialization `TypeManager` expects such property to be present inside a polymorphic object.
1165
- * Default value of discriminant is a `ClassName` which determined based on the type function.
1166
-
1167
- For proper deserialization of polymorphic types you have to provide such information inside your JSON.
1168
-
1169
- ```json
1170
- {
1171
- "shapes": [
1172
- {
1173
- "$type": "Rectangle",
1174
- "title": "Cool rectangle",
1175
- "width": 10,
1176
- "height": 10
1177
- },
1178
- {
1179
- "$type": "Square",
1180
- "title": "Perfect square",
1181
- "width": 10
1182
- },
1183
- {
1184
- "$type": "Circle",
1185
- "title": "Simple circle",
1186
- "radius": 6
1187
- }
1188
- ]
1189
- }
1190
- ```
1191
-
1192
- Now your JSON will be handled properly and you will get `Rectangle`, `Square` and `Circle` class instances in return.
1193
-
1194
- 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.
1195
-
1196
- ```json
1197
- {
1198
- "shapes": [
1199
- {
1200
- "$type": "Company.Api.Entities.Rectangle",
1201
- "title": "Cool rectangle",
1202
- "width": 10,
1203
- "height": 10
1204
- },
1205
- {
1206
- "$type": "Company.Api.Entities.Square",
1207
- "title": "Perfect square",
1208
- "width": 10
1209
- },
1210
- {
1211
- "$type": "Company.Api.Entities.Circle",
1212
- "title": "Simple circle",
1213
- "radius": 6
1214
- }
1215
- ]
1216
- }
1217
- ```
1218
-
1219
- To change `Discriminator` globally you have to use `TypeManager` configure method.
1220
-
1221
- ```typescript
1222
- import { TypeManagerOptions, TypeOptionsBase } from '@dipscope/type-manager';
1223
-
1224
- const typeOptionsBase: TypeOptionsBase<any> = {
1225
- discriminator: '$customType'
1226
- };
1227
-
1228
- const typeManagerOptions: TypeManagerOptions = {
1229
- typeOptionsBase: typeOptionsBase
1230
- };
1231
-
1232
- TypeManager.configure(typeManagerOptions);
1233
- ```
1234
-
1235
- To change `Discriminant` you have to use per type configuration.
1236
-
1237
- ```typescript
1238
- import { TypeManagerOptions, TypeFn, TypeOptions, PropertyName, PropertyOptions } from '@dipscope/type-manager';
1239
-
1240
- const rectangleOptions: TypeOptions<Rectangle> = {
1241
- discriminant: 'Company.Api.Entities.Rectangle'
1242
- };
1243
-
1244
- const squareOptions: TypeOptions<Square> = {
1245
- discriminant: 'Company.Api.Entities.Square'
1246
- };
1247
-
1248
- const circleOptions: TypeOptions<Circle> = {
1249
- discriminant: 'Company.Api.Entities.Circle'
1250
- };
1251
-
1252
- const typeManagerOptions: TypeManagerOptions = {
1253
- typeOptionsMap: new Map<TypeFn<any>, TypeOptions<any>>(
1254
- [Rectangle, rectangleOptions],
1255
- [Square, squareOptions],
1256
- [Circle, circleOptions]
1257
- )
1258
- };
1259
-
1260
- TypeManager.configure(typeManagerOptions);
1261
- ```
1262
-
1263
- As an alternative you can change `Discriminant` as the following using decorators.
1264
-
1265
- ```typescript
1266
- import { Type, Property } from '@dipscope/type-manager';
1267
-
1268
- @Type({
1269
- discriminant: 'Company.Api.Entities.Shape'
1270
- })
1271
- export abstract class Shape
1272
- {
1273
- @Property(String) public title: string;
1274
- }
1275
-
1276
- @Type({
1277
- discriminant: 'Company.Api.Entities.Rectangle'
1278
- })
1279
- export class Rectangle extends Shape
1280
- {
1281
- @Property(Number) public width: number;
1282
- @Property(Number) public height: number;
1283
- }
1284
-
1285
- @Type({
1286
- discriminant: 'Company.Api.Entities.Square'
1287
- })
1288
- export class Square extends Shape
1289
- {
1290
- @Property(Number) public width: number;
1291
- }
1292
-
1293
- @Type({
1294
- discriminant: 'Company.Api.Entities.Circle'
1295
- })
1296
- export class Circle extends Shape
1297
- {
1298
- @Property(Number) public radius: number;
1299
- }
1300
- ```
1301
-
1302
- 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.
1303
-
1304
- ```typescript
1305
- import { TypeManagerOptions, TypeOptionsBase } from '@dipscope/type-manager';
1306
-
1307
- const typeOptionsBase: TypeOptionsBase<any> = {
1308
- preserveDiscriminator: true
1309
- };
1310
-
1311
- const typeManagerOptions: TypeManagerOptions = {
1312
- typeOptionsBase: typeOptionsBase
1313
- };
1314
-
1315
- TypeManager.configure(typeManagerOptions);
1316
- ```
1317
-
1318
- With this option enabled discriminator will be present in output data.
1319
-
1320
- ### Configuring naming convention
1321
-
1322
- 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.
1323
-
1324
- ```typescript
1325
- import { Type, Property } from '@dipscope/type-manager';
1326
-
1327
- @Type()
1328
- export class User
1329
- {
1330
- @Property(String) public name: string;
1331
- @Property(Number) public loginCount: number;
1332
- @Property(DateTime) public createdAt: DateTime;
1333
- }
1334
- ```
1335
-
1336
- Our JSON should match the same naming convention to work properly.
1337
-
1338
- ```json
1339
- [
1340
- {
1341
- "name": "Dmitry",
1342
- "loginCount": 10,
1343
- "createdAt": "2021-02-22T20:15:00.000Z"
1344
- },
1345
- {
1346
- "name": "Alex",
1347
- "loginCount": 25,
1348
- "createdAt": "2021-02-22T21:15:00.000Z"
1349
- },
1350
- {
1351
- "name": "Anna",
1352
- "loginCount": 3,
1353
- "createdAt": "2021-02-22T21:15:23.000Z"
1354
- }
1355
- ]
1356
- ```
1357
-
1358
- But what to do if we don't control the JSON naming convention so it comes to us in a snake case?
1359
-
1360
- ```json
1361
- [
1362
- {
1363
- "name": "Dmitry",
1364
- "login_count": 10,
1365
- "created_at": "2021-02-22T20:15:00.000Z"
1366
- },
1367
- {
1368
- "name": "Alex",
1369
- "login_count": 25,
1370
- "created_at": "2021-02-22T21:15:00.000Z"
1371
- },
1372
- {
1373
- "name": "Anna",
1374
- "login_count": 3,
1375
- "created_at": "2021-02-22T21:15:23.000Z"
1376
- }
1377
- ]
1378
- ```
1379
-
1380
- We can still parse such a JSON by specifying an alias for each property but this will become a pain in a while.
1381
-
1382
- ```typescript
1383
- import { Type, Property } from '@dipscope/type-manager';
1384
-
1385
- @Type()
1386
- export class User
1387
- {
1388
- @Property(String) public name: string;
1389
- @Property(Number, { alias: 'login_count' }) public loginCount: number;
1390
- @Property(DateTime, { alias: 'created_at' }) public createdAt: DateTime;
1391
- }
1392
- ```
1393
-
1394
- `TypeManager` supports several naming conventions we can use to avoid dealing with aliases.
1395
-
1396
- * Camel case [camelCase];
1397
- * Flat case [flatcase];
1398
- * Flat upper case [FLATCASE];
1399
- * Kebab case [kebab-kase];
1400
- * Kebab upper case [KEBAB-CASE];
1401
- * Pascal case [PascalCase];
1402
- * Snake case [snake_case];
1403
- * Snake upper case [SNAKE_CASE];
1404
-
1405
- To set one we have to configure global options.
1406
-
1407
- ```typescript
1408
- import { TypeManagerOptions, TypeOptionsBase, SnakeCaseNamingConvention } from '@dipscope/type-manager';
1409
-
1410
- const typeOptionsBase: TypeOptionsBase<any> = {
1411
- namingConvention: new SnakeCaseNamingConvention()
1412
- };
1413
-
1414
- const typeManagerOptions: TypeManagerOptions = {
1415
- typeOptionsBase: typeOptionsBase
1416
- };
1417
-
1418
- TypeManager.configure(typeManagerOptions);
1419
- ```
1420
-
1421
- 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.
1422
-
1423
- ### Configuring reference handler
1424
-
1425
- Reference handler defines how references to the same objects including a circular one should be treated. We have several reference handlers: direct, lead and path. Each of them can be used globally or per type.
1426
-
1427
- * Direct reference handler preserves object references without making any special changes;
1428
- * Path reference handler preserves object references using JSONPath notation;
1429
- * Lead reference handler preserves object references excluding a circular one. When circular reference is detected it will be set to undefined;
1430
-
1431
- There is nothing better to show the difference than code. For example we have two classes which reference each other:
1432
-
1433
- ```typescript
1434
- import { Type, Property } from '@dipscope/type-manager';
1435
-
1436
- @Type()
1437
- class User
1438
- {
1439
- @Property(() => Company) public company: Company;
1440
- }
1441
-
1442
- @Type()
1443
- class Company
1444
- {
1445
- @Property(() => User) public user: User;
1446
- }
1447
- ```
1448
-
1449
- Somewhere in code you have such a logic:
1450
-
1451
- ```typescript
1452
- import { TypeManager } from '@dipscope/type-manager';
1453
-
1454
- const user = new User();
1455
- const company = new Company();
1456
-
1457
- user.company = company;
1458
- company.user = user;
1459
-
1460
- const result = TypeManager.serialize(User, user);
1461
- ```
1462
-
1463
- Here are results returned by different reference handlers:
1464
-
1465
- ```typescript
1466
- // Direct reference handler...
1467
- { company: { user: result } };
1468
-
1469
- // Path reference handler...
1470
- { company: { user: { $ref: '$' } } };
1471
-
1472
- // Lead reference handler...
1473
- { company: { user: undefined };
1474
- ```
1475
-
1476
- 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.
1477
-
1478
- ```typescript
1479
- import { TypeManagerOptions, TypeOptionsBase, PathReferenceHandler } from '@dipscope/type-manager';
1480
-
1481
- const typeOptionsBase: TypeOptionsBase<any> = {
1482
- referenceHandler: new PathReferenceHandler()
1483
- };
1484
-
1485
- const typeManagerOptions: TypeManagerOptions = {
1486
- typeOptionsBase: typeOptionsBase
1487
- };
1488
-
1489
- TypeManager.configure(typeManagerOptions);
1490
- ```
1491
-
1492
- With such configuration any reference will be handled using JSONPath so you are completely free from errors during conversion to a string.
1493
-
1494
- ## Advanced usage
1495
-
1496
- Our goal is to cover as much use cases as possible without making you to write additional code but there always be an application specific case. With that in mind we allow you to customize and extend each part of our pipeline.
1497
-
1498
- ### Defining custom data
1499
-
1500
- You can attach you custom metadata to our decorators using `customData` option available on `Type` and `Property`.
1501
-
1502
- ```typescript
1503
- import { Type, Property } from '@dipscope/type-manager';
1504
-
1505
- @Type({
1506
- customData: { rank: 1 }
1507
- })
1508
- class User
1509
- {
1510
- @Property(String, { customData: { priority: 10 } }) public name: string;
1511
- }
1512
- ```
1513
-
1514
- 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`.
1515
-
1516
- ```typescript
1517
- import { TypeManager } from '@dipscope/type-manager';
1518
-
1519
- const userMetadata = TypeManager.extractTypeMetadata(User);
1520
- const customData = userMetadata.customData;
1521
-
1522
- // Do something with type custom data...
1523
-
1524
- for (const propertyMetadata of userMetadata.propertyMetadataMap.values())
1525
- {
1526
- const propertyCustomData = propertyMetadata.customData;
1527
-
1528
- // Do something with property custom data...
1529
- }
1530
- ```
1531
-
1532
- ### Defining custom serializer
1533
-
1534
- 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.
1535
-
1536
- ```typescript
1537
- import { Serializer, TypeLike, SerializerContext } from '@dipscope/type-manager';
1538
- import { Fn } from '@app/module';
1539
-
1540
- export class DateTimeSerializer implements Serializer<DateTime>
1541
- {
1542
- public serialize(x: TypeLike<DateTime>, serializerContext: SerializerContext<DateTime>): TypeLike<string>
1543
- {
1544
- if (Fn.isUndefined(x))
1545
- {
1546
- return serializerContext.serializedDefaultValue;
1547
- }
1548
-
1549
- if (Fn.isNull(x))
1550
- {
1551
- return x;
1552
- }
1553
-
1554
- if (Fn.isDateTime(x))
1555
- {
1556
- return x.toIsoString();
1557
- }
1558
-
1559
- if (serializerContext.log.errorEnabled)
1560
- {
1561
- serializerContext.log.error(`${serializerContext.path}: cannot serialize value as date time.`, x);
1562
- }
1563
-
1564
- return undefined;
1565
- }
1566
-
1567
- public deserialize(x: TypeLike<string>, serializerContext: SerializerContext<DateTime>): TypeLike<DateTime>
1568
- {
1569
- if (Fn.isUndefined(x))
1570
- {
1571
- return serializerContext.deserializedDefaultValue;
1572
- }
1573
-
1574
- if (Fn.isNull(x))
1575
- {
1576
- return x;
1577
- }
1578
-
1579
- if (Fn.isString(x))
1580
- {
1581
- return DateTime.fromIsoString(x);
1582
- }
1583
-
1584
- if (serializerContext.log.errorEnabled)
1585
- {
1586
- serializerContext.log.error(`${serializerContext.path}: cannot deserialize value as date time.`, x);
1587
- }
1588
-
1589
- return undefined;
1590
- }
1591
- }
1592
- ```
1593
-
1594
- 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.
1595
-
1596
- 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.
1597
-
1598
- When you are finished with definitions there are two possible ways to register a serializer. You can use decorators.
1599
-
1600
- ```typescript
1601
- import { Type, Serializer } from '@dipscope/type-manager';
1602
-
1603
- @Type({
1604
- serializer: new DateTimeSerializer()
1605
- })
1606
- export class DateTime
1607
- {
1608
- ...
1609
- }
1610
- ```
1611
-
1612
- Or declarative configuration.
1613
-
1614
- ```typescript
1615
- import { TypeManager } from '@dipscope/type-manager';
1616
-
1617
- TypeManager.configureTypeOptions(DateTime, {
1618
- alias: 'DateTime',
1619
- serializer: new DateTimeSerializer()
1620
- });
1621
- ```
1622
-
1623
- With declarative configuration it is possible to override built in serializers if it's behaviour not suitable for your use cases. Also you can register serializers for types we not yet cover.
1624
-
1625
- ### Defining custom injector
1626
-
1627
- In modern world we are always use some kind of framework to build our application. It is definitely already have a configured dependency injection container so let's configure `TypeManager` for using it instead of build in one. You have to implement `Injector` interface with only one method. Here how it may look like in `Angular`.
1628
-
1629
- ```typescript
1630
- import { Injector, TypeMetadata } from '@dipscope/type-manager';
1631
- import { Injector as AngularInjector } from '@angular/core';
1632
-
1633
- export class CustomInjector implements Injector
1634
- {
1635
- private readonly angularInjector: AngularInjector;
1636
-
1637
- public constructor(angularInjector: AngularInjector)
1638
- {
1639
- this.angularInjector = angularInjector;
1640
-
1641
- return;
1642
- }
1643
-
1644
- public get<TType>(typeMetadata: TypeMetadata<TType>): TType | undefined
1645
- {
1646
- return this.angularInjector.get(typeMetadata.typeFn);
1647
- }
1648
- }
1649
- ```
1650
-
1651
- In general you have to get framework injector or create it manually. Then call method to get and instance by type when one is requested. Implementations can differ but we hope idea is clear. When you are finished with definitions you have to register custom injector for a `TypeManager`.
1652
-
1653
- ```typescript
1654
- import { TypeManager } from '@dipscope/type-manager';
1655
- import { Injector as AngularInjector } from '@angular/core';
1656
-
1657
- const angularInjector: AngularInjector = ...; // Get framework injector in core module for example.
1658
-
1659
- TypeManager.configureTypeOptionsBase({
1660
- injector: new CustomInjector(angularInjector)
1661
- });
1662
- ```
1663
-
1664
- Now types will be resolved using framework injector.
1665
-
1666
- ### Defining custom factory
1667
-
1668
- When you want to apply additional logic to how types are constructed you can specify custom factory globally or per type. Let's say you want to init some properties based on your custom data specified for a type. You can do this by extending default `TypeFactory`.
1669
-
1670
- ```typescript
1671
- import { TypeContext, Injector, TypeFactory } from '@dipscope/type-manager';
1672
-
1673
- export class CustomTypeFactory extends TypeFactory
1674
- {
1675
- public build<TType>(typeContext: TypeContext<TType>, injector: Injector): TType
1676
- {
1677
- // Build any type.
1678
- const type = super.build(typeContext, injector) as any;
1679
-
1680
- // Resolve custom data.
1681
- const typeMetadata = typeContext.typeMetadata;
1682
- const customData = typeMetadata.customData;
1683
-
1684
- // Process custom data.
1685
- for (const propertyName in customData)
1686
- {
1687
- type[propertyName] = customData[propertyName];
1688
- }
1689
-
1690
- return type;
1691
- }
1692
- }
1693
- ```
1694
-
1695
- When you are finished with definitions there are two possible ways to register a factory. You can use decorators.
1696
-
1697
- ```typescript
1698
- import { Type, Factory } from '@dipscope/type-manager';
1699
-
1700
- @Type({
1701
- customData: { rank: 1 },
1702
- factory: new CustomTypeFactory()
1703
- })
1704
- export class User
1705
- {
1706
- ...
1707
- }
1708
- ```
1709
-
1710
- Or declarative configuration.
1711
-
1712
- ```typescript
1713
- import { TypeManager } from '@dipscope/type-manager';
1714
-
1715
- // Overriding only for user type.
1716
- TypeManager.configureTypeOptions(User, {
1717
- customData: { rank: 1 },
1718
- factory: new CustomTypeFactory()
1719
- });
1720
-
1721
- // Overriding for any type.
1722
- TypeManager.configureTypeOptionsBase({
1723
- factory: new CustomTypeFactory()
1724
- });
1725
- ```
1726
-
1727
- ### Defining custom naming convention
1728
-
1729
- To define custom naming convention you have to implement `NamingConvention` interface with only one `convert` method. Here is an example implementation of camel case naming convention.
1730
-
1731
- ```typescript
1732
- import { NamingConvention, getWords } from '@dipscope/type-manager';
1733
-
1734
- export class CamelCaseNamingConvention implements NamingConvention
1735
- {
1736
- public convert(name: string): string
1737
- {
1738
- return getWords(name).reduce(this.reduce, '');
1739
- }
1740
-
1741
- private reduce(result: string, word: string, index: number): string
1742
- {
1743
- if (word.length === 0)
1744
- {
1745
- return result;
1746
- }
1747
-
1748
- const wordLowerCased = word.toLowerCase();
1749
-
1750
- if (index === 0)
1751
- {
1752
- return wordLowerCased;
1753
- }
1754
-
1755
- return `${result}${wordLowerCased[0].toUpperCase()}${wordLowerCased.slice(1)}`;
1756
- }
1757
- }
1758
- ```
1759
-
1760
- Public `convert` method receives a property name as it declared in a class. You have to call internal `getWords` function on it which will split property name into array of the words. In the `reduce` function you can combine this words to whatever string you want. When you are finished with definitions you have to register custom naming convention for a `TypeManager`.
1761
-
1762
- ```typescript
1763
- import { TypeManager } from '@dipscope/type-manager';
1764
-
1765
- TypeManager.configureTypeOptionsBase({
1766
- namingConvention: new CamelCaseNamingConvention()
1767
- });
1768
- ```
1769
-
1770
- Now property names will be resolved using your custom naming convention.
1771
-
1772
- ## Use cases
1773
-
1774
- 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.
1775
-
1776
- ### Built in serializers
1777
-
1778
- Here is a list of types with built in serializers.
1779
-
1780
- * Array;
1781
- * ArrayBuffer;
1782
- * Boolean;
1783
- * DataView;
1784
- * Date;
1785
- * Float32Array;
1786
- * Float64Array;
1787
- * Int8Array;
1788
- * Int16Array;
1789
- * Int32Array;
1790
- * Map;
1791
- * Number;
1792
- * Set;
1793
- * String;
1794
- * Uint8Array;
1795
- * Uint8ClampedArray;
1796
- * Uint16Array;
1797
- * Uint32Array;
1798
-
1799
- 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.
1800
-
1801
- ### Circular object references
1802
-
1803
- 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.
1804
-
1805
- ### Dependency injection and immutable types
1806
-
1807
- Follow the steps described in [inject decorator](#inject-decorator) section. To integrate our injection system with you yours check [defining custom injector](#defining-custom-injector) section.
1808
-
1809
- ### Different case usage in class and JSON
1810
-
1811
- 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.
1812
-
1813
- ### Enum types
1814
-
1815
- 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).
1816
-
1817
- ### Generic types
1818
-
1819
- Generic types are supported and you can define them. Read [property decorator](#property-decorator) section for more info.
1820
-
1821
- ### Integration with Angular
1822
-
1823
- With `Angular` you do not need to install [reflect-metadata](https://github.com/rbuckton/reflect-metadata) as it is already included in `core-js`. However, you still need to instruct CLI to include it in the build. This can be achieved simply by adding `import 'reflect-metadata';` to you `main.ts` file.
1824
-
1825
- 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.
1826
-
1827
- ### Polymorphic types
1828
-
1829
- 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.
1830
-
1831
- ## Versioning
1832
-
1833
- We use [SemVer](http://semver.org/) for versioning. For the versions available, see the versions section on [NPM project page](https://www.npmjs.com/package/@dipscope/type-manager).
1834
-
1835
- See information about breaking changes, release notes and migration steps between versions in [CHANGELOG.md](https://github.com/dipscope/TypeManager.TS/blob/master/CHANGELOG.md) file.
1836
-
1837
- ## Contributing
1838
-
1839
- Please read [CONTRIBUTING.md](https://github.com/dipscope/TypeManager.TS/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
1840
-
1841
- ## Authors
1842
-
1843
- * **Dmitry Pimonov** - *Initial work* - [dpimonov](https://github.com/dpimonov)
1844
-
1845
- See also the list of [contributors](https://github.com/dipscope/TypeManager.TS/contributors) who participated in this project.
1846
-
1847
- ## Notes
1848
-
1849
- Thanks for checking this package.
1850
-
1851
- Feel free to create an issue if you find any mistakes in documentation or have any improvements in mind.
1852
-
1853
- We wish you good luck and happy coding!
1854
-
1855
- ## License
1856
-
1857
- This project is licensed under the Apache 2.0 License - see the [LICENSE.md](https://github.com/dipscope/TypeManager.TS/blob/master/LICENSE.md) file for details.
1
+ # TypeManager.TS
2
+
3
+ ![GitHub](https://img.shields.io/github/license/dipscope/TypeManager.TS) ![NPM](https://img.shields.io/npm/v/@dipscope/type-manager) ![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)
4
+
5
+ Type manager is a superb parsing package for `TypeScript` which will help you to transform JSON strings or plain objects directly into class instances. No more need for unsafe `JSON.parse` and `JSON.stringify` functions. Forget about manual mapping and limitations. We support data transformations, circular references, naming conventions, generic and polymorphic types. Parsing never was so fun and easy.
6
+
7
+ Configuration can be done using [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) or declarative configuration for your or 3rd party classes.
8
+
9
+ 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.
10
+
11
+ ## Give a star :star:
12
+
13
+ If you like or are using this project please give it a star. Thanks!
14
+
15
+ ## Table of contents
16
+
17
+ * [What issues it solves?](#what-issues-it-solves)
18
+ * [Installation](#installation)
19
+ * [How it works?](#how-it-works)
20
+ * [Defining decorators](#defining-decorators)
21
+ * [Type decorator](#type-decorator)
22
+ * [Property decorator](#property-decorator)
23
+ * [Inject decorator](#inject-decorator)
24
+ * [Defining decorator options](#defining-decorator-options)
25
+ * [Alias option](#alias-option)
26
+ * [Custom data option](#custom-data-option)
27
+ * [Default value option](#default-value-option)
28
+ * [Deserializable option](#deserializable-option)
29
+ * [Discriminant option](#discriminant-option)
30
+ * [Discriminator option](#discriminator-option)
31
+ * [Factory option](#factory-option)
32
+ * [Injectable option](#injectable-option)
33
+ * [Injector option](#injector-option)
34
+ * [Naming convention option](#naming-convention-option)
35
+ * [Preserve discriminator option](#preserve-discriminator-option)
36
+ * [Preserve null option](#preserve-null-option)
37
+ * [Reference handler option](#reference-handler-option)
38
+ * [Serializable option](#serializable-option)
39
+ * [Serializer option](#serializer-option)
40
+ * [Use default value option](#use-default-value-option)
41
+ * [Use implicit conversion option](#use-implicit-conversion-option)
42
+ * [Defining configuration manually](#defining-configuration-manually)
43
+ * [Configuring global options](#configuring-global-options)
44
+ * [Configuring options per type](#configuring-options-per-type)
45
+ * [Configuring usage of polymorphic types](#configuring-usage-of-polymorphic-types)
46
+ * [Configuring naming convention](#configuring-naming-convention)
47
+ * [Configuring reference handler](#configuring-reference-handler)
48
+ * [Advanced usage](#advanced-usage)
49
+ * [Defining custom data](#defining-custom-data)
50
+ * [Defining custom serializer](#defining-custom-serializer)
51
+ * [Defining custom injector](#defining-custom-injector)
52
+ * [Defining custom factory](#defining-custom-factory)
53
+ * [Defining custom naming convention](#defining-custom-naming-convention)
54
+ * [Use cases](#use-cases)
55
+ * [Built in serializers](#built-in-serializers)
56
+ * [Circular object references](#circular-object-references)
57
+ * [Dependency injection and immutable types](#dependency-injection-and-immutable-types)
58
+ * [Different case usage in class and JSON](#different-case-usage-in-class-and-json)
59
+ * [Enum types](#enum-types)
60
+ * [Generic types](#generic-types)
61
+ * [Integration with Angular](#integration-with-angular)
62
+ * [Polymorphic types](#polymorphic-types)
63
+ * [Versioning](#versioning)
64
+ * [Contributing](#contributing)
65
+ * [Authors](#authors)
66
+ * [Notes](#notes)
67
+ * [License](#license)
68
+
69
+ ## What issues it solves?
70
+
71
+ In `JavaScript` there are two types of objects:
72
+
73
+ * Plain objects which are created using `{}` notation;
74
+ * Constructor based objects which are created using `new Class()` notation.
75
+
76
+ Sometimes we want to transform plain objects to the classes we have. Let's assume we are loading a users JSON data from our backend API or the other datasource:
77
+
78
+ ```json
79
+ [
80
+ {
81
+ "id": 1,
82
+ "name": "Dmitry",
83
+ "deletedAt": "2021-02-22T20:15:00.000Z"
84
+ },
85
+ {
86
+ "id": 2,
87
+ "name": "Alex",
88
+ "deletedAt": null
89
+ },
90
+ {
91
+ "id": 3,
92
+ "name": "Anna",
93
+ "deletedAt": null
94
+ }
95
+ ]
96
+ ```
97
+
98
+ We have a `User` class:
99
+
100
+ ```typescript
101
+ export class User
102
+ {
103
+ public id: number;
104
+ public name: string;
105
+ public deletedAt: Date;
106
+
107
+ public constructor(id: number, name: string)
108
+ {
109
+ this.id = id;
110
+ this.name = name;
111
+
112
+ return;
113
+ }
114
+
115
+ public isDeleted(): boolean
116
+ {
117
+ return this.deletedAt !== null;
118
+ }
119
+ }
120
+ ```
121
+
122
+ Somewhere in code we are checking if user is deleted before performing some action.
123
+
124
+ ```typescript
125
+ const users: User[] = JSON.parse(usersJson);
126
+
127
+ for (const user of users)
128
+ {
129
+ if (user.isDeleted())
130
+ {
131
+ // Do some action...
132
+ }
133
+ }
134
+ ```
135
+
136
+ Do you see the problem in this piece of code?
137
+
138
+ `JSON.parse` function returns plain objects and we actually lied to compiler when said that it is a `User` class array. In this code we can successfully use `user.id` or `user.name`. However we cannot use `user.isDeleted()` because user is not an instance of a `User` class.
139
+
140
+ So what to do? How to get `User` array from our JSON?
141
+
142
+ 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.
143
+
144
+ There exists much more simple way. Let's use our `TypeManager` for getting instances of `User` class from JSON:
145
+
146
+ ```typescript
147
+ import { TypeManager } from '@dipscope/type-manager';
148
+
149
+ const users: User[] = TypeManager.parse(User, usersJson);
150
+
151
+ for (const user of users)
152
+ {
153
+ if (user.isDeleted())
154
+ {
155
+ // Do some action...
156
+ }
157
+ }
158
+ ```
159
+
160
+ Now we can use all power provided by `JavaScript` class instances.
161
+
162
+ Furthermore `TypeManager.TS` provides you:
163
+
164
+ * Reflection abilities at runtime;
165
+ * Support for generic types;
166
+ * Handling of cyclic object references and different ways of serialization when they appear;
167
+ * Ability to configure serialization of 3rd party classes;
168
+ * Support for polymorphic types;
169
+ * Great alternative for similar packages like [class-transformer](https://github.com/typestack/class-transformer), [TypedJSON](https://github.com/JohnWeisz/TypedJSON) and [jackson-js](https://github.com/pichillilorenzo/jackson-js);
170
+
171
+ Want to know more? Let's dive into the details.
172
+
173
+ ## Installation
174
+
175
+ `TypeManager.TS` is available from NPM, both for browser (e.g. using webpack) and NodeJS:
176
+
177
+ ```
178
+ npm i @dipscope/type-manager
179
+ ```
180
+
181
+ TypeScript needs to run with the `experimentalDecorators` and `emitDecoratorMetadata` options enabled when using decorator annotations. So make sure you have properly configured your `tsconfig.json` file.
182
+
183
+ _If you want additional type-safety and reduced syntax you may wish to install [reflect-metadata](https://github.com/rbuckton/reflect-metadata). This step is on your choice and fully optional. When installed it must be available globally to work. This can usually be done with `import 'reflect-metadata';` in your main index file._
184
+
185
+ ## How it works?
186
+
187
+ 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:
188
+
189
+ * Using decorator annotations;
190
+ * Using declarative style;
191
+
192
+ 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.
193
+
194
+ Let's have a look at the most simple example of configuration using decorators.
195
+
196
+ ```typescript
197
+ import { Type, Property } from '@dipscope/type-manager';
198
+
199
+ @Type()
200
+ export class User
201
+ {
202
+ @Property(String) public name: string;
203
+ @Property(String) public email: string;
204
+ }
205
+ ```
206
+
207
+ 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.
208
+
209
+ The same configuration can be rewritten using declarative style.
210
+
211
+ ```typescript
212
+ import { TypeManager, TypeMetadata, TypeConfiguration } from '@dipscope/type-manager';
213
+
214
+ export class User
215
+ {
216
+ public name: string;
217
+ public email: string;
218
+ }
219
+
220
+ export class UserConfiguration implements TypeConfiguration<User>
221
+ {
222
+ public configure(typeMetadata: TypeMetadata<User>): void
223
+ {
224
+ typeMetadata.configurePropertyMetadata('name')
225
+ .hasTypeArgument(String);
226
+
227
+ typeMetadata.configurePropertyMetadata('email')
228
+ .hasTypeArgument(String);
229
+
230
+ return;
231
+ }
232
+ }
233
+
234
+ TypeManager.applyTypeConfiguration(User, new UserConfiguration());
235
+ ```
236
+
237
+ As you can see now our `User` class defined without decorators. Instead you have to call `TypeManager` method and provide `TypeConfiguration` related to `User` type.
238
+
239
+ 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.
240
+
241
+ ```typescript
242
+ import { TypeManager } from '@dipscope/type-manager';
243
+
244
+ const userObject = TypeManager.serialize(User, new User());
245
+ const user = TypeManager.deserialize(User, userObject);
246
+
247
+ user instanceof User; // True.
248
+ ```
249
+
250
+ Calling serialize creates a plain object and deserialize creates an instance of `User` class. During deserialize you can provide any object. It's not nesassary that object was produced by `TypeManager`. If object is an `Array` you will get array of types in return. Objects are parsed based on general type configuration defined by developer. It is also possible to stringify and parse JSON.
251
+
252
+ ```typescript
253
+ import { TypeManager } from '@dipscope/type-manager';
254
+
255
+ const userJson = TypeManager.stringify(User, new User());
256
+ const user = TypeManager.parse(User, userJson);
257
+
258
+ user instanceof User; // True.
259
+ ```
260
+
261
+ Stringify and parse functions are wrappers over native JSON class functions. In addition they add serialize and deserialize support under the hood.
262
+
263
+ Static functions are not the only way to work with a `TypeManager`. You can also work on instance based manner.
264
+
265
+ ```typescript
266
+ import { TypeManager } from '@dipscope/type-manager';
267
+
268
+ const typeManager = new TypeManager();
269
+ const userObject = userManager.serialize(User, new User());
270
+ const user = userManager.deserialize(User, userObject);
271
+
272
+ user instanceof User; // True.
273
+ ```
274
+
275
+ 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 and have different serialization groups. By default all decorator based configurations and static calls are applied to the singleton type manager instance which is automatically created under the hood.
276
+
277
+ ## Defining decorators
278
+
279
+ There are few decorators which controls the main flow. This are `Type`, `Property` and `Inject` decorators. Let's go through each of them.
280
+
281
+ ### Type decorator
282
+
283
+ Type decorator defines a type and should be declared right before a class.
284
+
285
+ ```typescript
286
+ import { Type } from '@dipscope/type-manager';
287
+
288
+ @Type()
289
+ export class User
290
+ {
291
+ ...
292
+ }
293
+ ```
294
+
295
+ This will register a new type with default type serializer assigned to it. You can define how each class should be treated by providing optional configure options as a first argument.
296
+
297
+ ```typescript
298
+ import { Type } from '@dipscope/type-manager';
299
+
300
+ @Type({
301
+ alias: 'User',
302
+ serializer: new UserSerializer()
303
+ })
304
+ export class User
305
+ {
306
+ ...
307
+ }
308
+ ```
309
+
310
+ 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.
311
+
312
+ There are more options can be provided for a type, so check `TypeOptions` definition or section with [decorator options](#defining-decorator-options) below.
313
+
314
+ ### Property decorator
315
+
316
+ Property decorator defines per property configuration within a type and should be declared right before a property or accessor definition.
317
+
318
+ ```typescript
319
+ import 'reflect-metadata';
320
+ import { Type, Property } from '@dipscope/type-manager';
321
+
322
+ @Type()
323
+ export class User
324
+ {
325
+ @Property() public name: string;
326
+ }
327
+ ```
328
+
329
+ 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 it will result in direct serialization. For such a case you have to explicitly define a property type.
330
+
331
+ ```typescript
332
+ import { Type, Property } from '@dipscope/type-manager';
333
+
334
+ @Type()
335
+ export class User
336
+ {
337
+ @Property(String) public name: string;
338
+ }
339
+ ```
340
+
341
+ Depending on your use case there are two possible ways to configure additional property options.
342
+
343
+ If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) then provide options as a first argument.
344
+
345
+ ```typescript
346
+ import 'reflect-metadata';
347
+ import { Type, Property } from '@dipscope/type-manager';
348
+
349
+ @Type()
350
+ export class User
351
+ {
352
+ @Property({ alias: 'username' }) public name: string;
353
+ }
354
+ ```
355
+
356
+ If types defined explicitly then provide options as a second argument.
357
+
358
+ ```typescript
359
+ import { Type, Property } from '@dipscope/type-manager';
360
+
361
+ @Type()
362
+ export class User
363
+ {
364
+ @Property(String, { alias: 'username' }) public name: string;
365
+ }
366
+ ```
367
+
368
+ 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 [decorator options](#defining-decorator-options) below. For example you can make some properties serializable only or define custom property serialization.
369
+
370
+ 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.
371
+
372
+ 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.
373
+
374
+ ```typescript
375
+ import 'reflect-metadata';
376
+ import { Type, Property } from '@dipscope/type-manager';
377
+
378
+ @Type()
379
+ export class User
380
+ {
381
+ @Property([String, Number], { alias: 'myMap' }) public map: Map<string, number>;
382
+ }
383
+ ```
384
+
385
+ If types defined explicitly then provide generics as a second argument so configure options will become the third.
386
+
387
+ ```typescript
388
+ import { Type, Property } from '@dipscope/type-manager';
389
+
390
+ @Type()
391
+ export class User
392
+ {
393
+ @Property(Map, [String, Number], { alias: 'myMap' }) public map: Map<string, number>;
394
+ }
395
+ ```
396
+
397
+ 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.
398
+
399
+ 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.
400
+
401
+ ```typescript
402
+ import { Type, Property } from '@dipscope/type-manager';
403
+
404
+ @Type()
405
+ export class User
406
+ {
407
+ @Property({
408
+ typeArgument: Map,
409
+ genericArguments: [String, Number],
410
+ alias: 'myMap'
411
+ })
412
+ public map: Map<string, number>;
413
+ }
414
+ ```
415
+
416
+ Which syntax to use is completely on your choice. `Property` decorator is smart enough to setup everything based on usage.
417
+
418
+ 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.
419
+
420
+ ```typescript
421
+ import 'reflect-metadata';
422
+ import { Type, Property } from '@dipscope/type-manager';
423
+
424
+ @Type()
425
+ export class UserStatus
426
+ {
427
+ @Property() public title: string;
428
+ }
429
+
430
+ @Type()
431
+ export class User
432
+ {
433
+ @Property() public userStatus: UserStatus;
434
+ }
435
+ ```
436
+
437
+ With array of relations you have to use generics.
438
+
439
+ ```typescript
440
+ import 'reflect-metadata';
441
+ import { Type, Property } from '@dipscope/type-manager';
442
+
443
+ @Type()
444
+ export class UserStatus
445
+ {
446
+ @Property() public title: string;
447
+ }
448
+
449
+ @Type()
450
+ export class User
451
+ {
452
+ @Property([UserStatus]) public userStatuses: Array<UserStatus>;
453
+ }
454
+ ```
455
+
456
+ If types defined explicitly then definition will be the following.
457
+
458
+ ```typescript
459
+ import { Type, Property } from '@dipscope/type-manager';
460
+
461
+ @Type()
462
+ export class UserStatus
463
+ {
464
+ @Property(String) public title: string;
465
+ }
466
+
467
+ @Type()
468
+ export class User
469
+ {
470
+ @Property(UserStatus) public userStatus: UserStatus;
471
+ }
472
+ ```
473
+
474
+ Then for array of relations it will be the following.
475
+
476
+ ```typescript
477
+ import { Type, Property } from '@dipscope/type-manager';
478
+
479
+ @Type()
480
+ export class UserStatus
481
+ {
482
+ @Property(String) public title: string;
483
+ }
484
+
485
+ @Type()
486
+ export class User
487
+ {
488
+ @Property(Array, [UserStatus]) public userStatuses: Array<UserStatus>;
489
+ }
490
+ ```
491
+
492
+ If any type defines an alias - it can be used as a type resolver.
493
+
494
+ ```typescript
495
+ import { Type, Property } from '@dipscope/type-manager';
496
+
497
+ @Type({
498
+ alias: 'UserStatus'
499
+ })
500
+ export class UserStatus
501
+ {
502
+ @Property(String) public title: string;
503
+ }
504
+
505
+ @Type()
506
+ export class User
507
+ {
508
+ @Property('UserStatus') public userStatus: UserStatus;
509
+ }
510
+ ```
511
+
512
+ If you have circular reference or your type declared later an extended syntax can be used to lazily define a type.
513
+
514
+ ```typescript
515
+ import { Type, Property } from '@dipscope/type-manager';
516
+
517
+ @Type()
518
+ export class User
519
+ {
520
+ @Property(() => UserStatus) public userStatus: UserStatus;
521
+ }
522
+
523
+ @Type()
524
+ export class UserStatus
525
+ {
526
+ @Property(() => String) public title: string;
527
+ }
528
+ ```
529
+
530
+ 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.
531
+
532
+ 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).
533
+
534
+ 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.
535
+
536
+ ```typescript
537
+ import 'reflect-metadata';
538
+ import { Type, Property } from '@dipscope/type-manager';
539
+
540
+ export enum UserPriorityNumeric
541
+ {
542
+ Low,
543
+ Medium,
544
+ High
545
+ }
546
+
547
+ export enum UserPriorityTextual
548
+ {
549
+ Low = 'Low',
550
+ Medium = 'Medium',
551
+ High = 'High'
552
+ }
553
+
554
+ @Type()
555
+ export class User
556
+ {
557
+ @Property() public userPriorityNumeric: UserPriorityNumeric;
558
+ @Property() public userPriorityTextual: UserPriorityTextual;
559
+ }
560
+ ```
561
+
562
+ If types defined explicitly then definition will be the following.
563
+
564
+ ```typescript
565
+ import { Type, Property } from '@dipscope/type-manager';
566
+
567
+ export enum UserPriorityNumeric
568
+ {
569
+ Low,
570
+ Medium,
571
+ High
572
+ }
573
+
574
+ export enum UserPriorityTextual
575
+ {
576
+ Low = 'Low',
577
+ Medium = 'Medium',
578
+ High = 'High'
579
+ }
580
+
581
+ @Type()
582
+ export class User
583
+ {
584
+ @Property(Number) public userPriorityNumeric: UserPriorityNumeric;
585
+ @Property(String) public userPriorityTextual: UserPriorityTextual;
586
+ }
587
+ ```
588
+
589
+ One should remember this when explicitly defining types for enums.
590
+
591
+ ### Inject decorator
592
+
593
+ Inject decorator controls your type dependency and declared right before a constructor parameter.
594
+
595
+ ```typescript
596
+ import { Type, Inject } from '@dipscope/type-manager';
597
+
598
+ @Type()
599
+ export class User
600
+ {
601
+ public constructor(@Inject('name') name: string, @Inject(UserService) userService: UserService)
602
+ {
603
+ this.name = name;
604
+
605
+ // Any action with UserService...
606
+
607
+ return;
608
+ }
609
+ }
610
+ ```
611
+
612
+ It accepts two possible inputs as its argument.
613
+
614
+ * String key from JSON context;
615
+ * Certain type registered as injectable;
616
+
617
+ When a string key is provided then a certain value will be resolved from JSON context for you when creating an instance. If any property declares the same key you will recieve deserialized value. If not then original value will be provided instead. Injecting context values is a use case of immutable types.
618
+
619
+ When a certain type is provided it will be resolved from the dependency injection container. If you are going to use internal type injector then you should register injectable types as the following. By default singleton injector is used to resolve such services.
620
+
621
+ ```typescript
622
+ import { Type } from '@dipscope/type-manager';
623
+
624
+ @Type({
625
+ injectable: true
626
+ })
627
+ export class UserService
628
+ {
629
+ public property: string;
630
+ }
631
+ ```
632
+
633
+ 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. You can read more about [creating a custom injector](#defining-custom-injector) in a separate section.
634
+
635
+ If you are using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) the injection of services can be simplified.
636
+
637
+ ```typescript
638
+ import { Type, Inject } from '@dipscope/type-manager';
639
+
640
+ @Type()
641
+ export class User
642
+ {
643
+ public constructor(@Inject('name') name: string, userService: UserService)
644
+ {
645
+ this.name = name;
646
+
647
+ // Any action with UserService...
648
+
649
+ return;
650
+ }
651
+ }
652
+ ```
653
+
654
+ Note that now you don't have to specify injection for types explicitly. However injection of values by key from JSON context still present. It's because argument names cannot be resolved using reflection.
655
+
656
+ ## Defining decorator options
657
+
658
+ `Type` and `Property` decorators provide full configuration for your classes using configure options. In this section we will go through each of them.
659
+
660
+ ### Alias option
661
+
662
+ This option can be used both on type and property to define an alias.
663
+
664
+ ```typescript
665
+ import { Type, Property } from '@dipscope/type-manager';
666
+
667
+ @Type({
668
+ alias: 'User'
669
+ })
670
+ export class User
671
+ {
672
+ @Property(String, { alias: 'username' }) public name: string;
673
+ }
674
+ ```
675
+
676
+ Alias defined for a class can be used later for resolving property types. Note that it should be unique within application to work properly.
677
+
678
+ Alias defined for a property declares that property name differs from one specified in JSON. In our case `username` will be used instead of `name` during JSON serialization and deserialization.
679
+
680
+ ### Custom data option
681
+
682
+ This option can be used to provide any custom data for type or property.
683
+
684
+ ```typescript
685
+ import { Type, Property, CustomKey } from '@dipscope/type-manager';
686
+
687
+ const rankKey = new CustomKey<number>('rank');
688
+ const orderKey = new CustomKey<number>('order');
689
+
690
+ @Type({
691
+ customOptions: [
692
+ [rankKey, 1],
693
+ [orderKey, 2]
694
+ ]
695
+ })
696
+ export class User
697
+ {
698
+ @Property(String, { customOptions: [[orderKey, 3]]}) public name: string;
699
+ }
700
+ ```
701
+
702
+ 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.
703
+
704
+ ### Default value option
705
+
706
+ This option is used to define a default value when one is undefined. It can be used on type or property.
707
+
708
+ ```typescript
709
+ import { Type, Property } from '@dipscope/type-manager';
710
+
711
+ @Type({
712
+ defaultValue: () => new User()
713
+ })
714
+ export class User
715
+ {
716
+ @Property(String, { defaultValue: 'BestName' }) public name: string;
717
+ }
718
+ ```
719
+
720
+ This will affect both serialized and deserialized default value. This will fit perfectly for most types. You can also specify serialized and deserialized default value explicitly for complex types by using two other options.
721
+
722
+ ```typescript
723
+ import { Type, Property } from '@dipscope/type-manager';
724
+
725
+ @Type({
726
+ serializedDefaultValue: () => {},
727
+ deserializedDefaultValue: () => new User()
728
+ })
729
+ export class User
730
+ {
731
+ @Property(String, { serializedDefaultValue: 'SerializedName', deserializedDefaultValue: 'DeserializedName' }) public name: string;
732
+ }
733
+ ```
734
+
735
+ 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` option per type and property or enable globally using `TypeManager` configure method.
736
+
737
+ ### Deserializable option
738
+
739
+ This option is used to enable or disable deserialization for a certain property.
740
+
741
+ ```typescript
742
+ import { Type, Property } from '@dipscope/type-manager';
743
+
744
+ @Type()
745
+ export class User
746
+ {
747
+ @Property(String, { deserializable: true }) public name: string;
748
+ }
749
+ ```
750
+
751
+ By default all properties are deserializable.
752
+
753
+ ### Discriminant option
754
+
755
+ This option is used to define a custom discriminant for a type which is later used during serialization and deserialization of polymorphic types.
756
+
757
+ ```typescript
758
+ import { Type, Property } from '@dipscope/type-manager';
759
+
760
+ @Type({
761
+ discriminant: 'Company.Api.Entities.User'
762
+ })
763
+ export class User
764
+ {
765
+ @Property(String) public name: string;
766
+ }
767
+ ```
768
+
769
+ You can read more about handling of polymorphic types in this [section](#configuring-usage-of-polymorphic-types).
770
+
771
+ ### Discriminator option
772
+
773
+ This option can be used to define a custom property which stores discriminant of polymorphic type.
774
+
775
+ ```typescript
776
+ import { Type, Property } from '@dipscope/type-manager';
777
+
778
+ @Type({
779
+ discriminator: '__typename__'
780
+ })
781
+ export class User
782
+ {
783
+ @Property(String) public name: string;
784
+ }
785
+ ```
786
+
787
+ 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).
788
+
789
+ ### Factory option
790
+
791
+ This option can be used to register a handler which should be used for constructing a type instead of default one.
792
+
793
+ ```typescript
794
+ import { Type, Property } from '@dipscope/type-manager';
795
+
796
+ @Type({
797
+ factory: new UserFactory()
798
+ })
799
+ export class User
800
+ {
801
+ @Property(String) public name: string;
802
+ }
803
+ ```
804
+
805
+ 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.
806
+
807
+ ### Injectable option
808
+
809
+ This option is used to register a type in dependency injection container.
810
+
811
+ ```typescript
812
+ import { Type } from '@dipscope/type-manager';
813
+
814
+ @Type({
815
+ injectable: true
816
+ })
817
+ export class UserService
818
+ {
819
+ public property: string;
820
+ }
821
+ ```
822
+
823
+ Injectable type later can be provided as a dependency.
824
+
825
+ ```typescript
826
+ import { Type, Property, Inject } from '@dipscope/type-manager';
827
+
828
+ @Type()
829
+ export class User
830
+ {
831
+ @Property(String) public name: string;
832
+
833
+ public constructor(@Inject(UserService) userService: UserService)
834
+ {
835
+ // Any action with UserService...
836
+
837
+ return;
838
+ }
839
+ }
840
+ ```
841
+
842
+ 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. You can read more about [creating a custom injector](#defining-custom-injector) in a separate section.
843
+
844
+ ### Injector option
845
+
846
+ This option can be used to define a custom injector implementation which should be used in a type scope.
847
+
848
+ ```typescript
849
+ import { Type, Property } from '@dipscope/type-manager';
850
+
851
+ @Type({
852
+ injector: new UserInjector()
853
+ })
854
+ export class User
855
+ {
856
+ @Property(String) public name: string;
857
+ }
858
+ ```
859
+
860
+ 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.
861
+
862
+ ### Naming convention option
863
+
864
+ This option can be used both on type and property to provide custom naming strategy.
865
+
866
+ ```typescript
867
+ import { Type, Property, CamelCaseNamingConvention, SnakeCaseNamingConvention } from '@dipscope/type-manager';
868
+
869
+ @Type({
870
+ namingConvention: new CamelCaseNamingConvention()
871
+ })
872
+ export class User
873
+ {
874
+ @Property(String, { namingConvention: new SnakeCaseNamingConvention() }) public name: string;
875
+ }
876
+ ```
877
+
878
+ 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.
879
+
880
+ ### Preserve discriminator option
881
+
882
+ This option defines if discriminator should be preserved in objects during serialization and deserialization.
883
+
884
+ ```typescript
885
+ import { Type, Property } from '@dipscope/type-manager';
886
+
887
+ @Type({
888
+ preserveDiscriminator: true
889
+ })
890
+ export class User
891
+ {
892
+ @Property(String) public name: string;
893
+ }
894
+ ```
895
+
896
+ 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).
897
+
898
+ ### Preserve null option
899
+
900
+ This option defines if null values should be preserved during serialization and deserialization.
901
+
902
+ ```typescript
903
+ import { Type, Property } from '@dipscope/type-manager';
904
+
905
+ @Type({
906
+ preserveNull: true
907
+ })
908
+ export class User
909
+ {
910
+ @Property(String, { preserveNull: false }) public name: string;
911
+ }
912
+ ```
913
+
914
+ By default null values are preserved. You can set it to `false` per type, property or globally using `TypeManager` configure method. This will result in treating null values as undefined so you will get all related behaviours like setting default values.
915
+
916
+ ### Reference handler option
917
+
918
+ This option can be used both on type and property to specify how references to the same objects should be handled during serialization and deserialization.
919
+
920
+ ```typescript
921
+ import { Type, Property, CircularReferenceHandler, PlainReferenceHandler } from '@dipscope/type-manager';
922
+
923
+ @Type({
924
+ referenceHandler: new CircularReferenceHandler()
925
+ })
926
+ export class User
927
+ {
928
+ @Property(String, { referenceHandler: new PlainReferenceHandler() }) public name: string;
929
+ }
930
+ ```
931
+
932
+ 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.
933
+
934
+ ### Serializable option
935
+
936
+ This option is used to enable or disable serialization for a certain property.
937
+
938
+ ```typescript
939
+ import { Type, Property } from '@dipscope/type-manager';
940
+
941
+ @Type()
942
+ export class User
943
+ {
944
+ @Property(String, { serializable: true }) public name: string;
945
+ }
946
+ ```
947
+
948
+ By default all properties are serializable.
949
+
950
+ ### Serializer option
951
+
952
+ This option is used to define custom serializer for a type or property.
953
+
954
+ ```typescript
955
+ import { Type, Property } from '@dipscope/type-manager';
956
+
957
+ @Type({
958
+ serializer: new UserSerializer()
959
+ })
960
+ export class User
961
+ {
962
+ @Property(String, { serializer: new UserNameSerializer() }) public name: string;
963
+ }
964
+ ```
965
+
966
+ 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.
967
+
968
+ ### Use default value option
969
+
970
+ This option enables or disables using default value per type or property.
971
+
972
+ ```typescript
973
+ import { Type, Property } from '@dipscope/type-manager';
974
+
975
+ @Type({
976
+ useDefaultValue: true
977
+ })
978
+ export class User
979
+ {
980
+ @Property(String, { useDefaultValue: false }) public name: string;
981
+ }
982
+ ```
983
+
984
+ Using default values is turned off by default. You can enable them globally using `TypeManager` configure method.
985
+
986
+ ### Use implicit conversion option
987
+
988
+ 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.
989
+
990
+ ```typescript
991
+ import { Type, Property } from '@dipscope/type-manager';
992
+
993
+ @Type()
994
+ export class User
995
+ {
996
+ @Property(String, { useImplicitConversion: true }) public name: string;
997
+ }
998
+ ```
999
+
1000
+ 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` option per type and property or enable globally using `TypeManager` configure method.
1001
+
1002
+ ## Defining configuration manually
1003
+
1004
+ 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 configure methods.
1005
+
1006
+ 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.
1007
+
1008
+ ### Configuring global options
1009
+
1010
+ There are several options which can be configured globally. For example let's override using of default value option so we don't have to specify it explicitly per type or property.
1011
+
1012
+ ```typescript
1013
+ import { TypeManagerOptions, TypeOptionsBase } from '@dipscope/type-manager';
1014
+
1015
+ const typeOptionsBase: TypeOptionsBase<any> = {
1016
+ useDefaultValue: true
1017
+ };
1018
+
1019
+ const typeManagerOptions: TypeManagerOptions = {
1020
+ typeOptionsBase: typeOptionsBase
1021
+ };
1022
+
1023
+ TypeManager.configure(typeManagerOptions);
1024
+ ```
1025
+
1026
+ For the full list of available global options check `TypeOptionsBase` definition or follow the documentation as we are going to touch them while we proceed.
1027
+
1028
+ ### Configuring options per type
1029
+
1030
+ Here is an example of declarative configuration which can be used for 3rd party or your own classes. Basically you have exact same options as with decorators but configuration is done using method calls.
1031
+
1032
+ ```typescript
1033
+ import { DateTime } from '@external-library';
1034
+ import { TypeConfiguration, TypeMetadata, TypeManager } from '@dipscope/type-manager';
1035
+
1036
+ export class DateTimeConfiguration implements TypeConfiguration<DateTime>
1037
+ {
1038
+ public configure(typeMetadata: TypeMetadata<DateTime>): void
1039
+ {
1040
+ typeMetadata.hasAlias('DateTime')
1041
+ .hasSerializer(new DateTimeSerializer());
1042
+
1043
+ return;
1044
+ }
1045
+ }
1046
+
1047
+ export class UserConfiguration implements TypeConfiguration<User>
1048
+ {
1049
+ public configure(typeMetadata: TypeMetadata<User>): void
1050
+ {
1051
+ typeMetadata.hasAlias('User')
1052
+ .hasSerializer(new DateTimeSerializer());
1053
+
1054
+ typeMetadata.configurePropertyMetadata('name')
1055
+ .isSerializable()
1056
+ .hasAlias('username')
1057
+ .hasTypeArgument(String);
1058
+
1059
+ typeMetadata.configurePropertyMetadata('createdAt')
1060
+ .hasTypeArgument(DateTime);
1061
+
1062
+ return;
1063
+ }
1064
+ }
1065
+
1066
+ TypeManager.applyTypeConfiguration(DateTime, new DateTimeConfiguration())
1067
+ .applyTypeConfiguration(User, new UserConfiguration());
1068
+ ```
1069
+
1070
+ There is a well defined order to how type options are applied when using configure methods on one type. One should remember this when combining and overriding options in different places.
1071
+
1072
+ 1. Base type options are applied;
1073
+ 2. Decorator type options are applied;
1074
+ 3. Declarative type options are applied;
1075
+ 4. Property type options are applied;
1076
+
1077
+ With declarative configuration you can declare types like the following and keep metadata in another place.
1078
+
1079
+ ```typescript
1080
+ export class User
1081
+ {
1082
+ public name: string;
1083
+ public createdAt: DateTime;
1084
+ }
1085
+ ```
1086
+
1087
+ Also if you are declaring only 3rd party classes the use case can be the following with [reflect-metadata](https://github.com/rbuckton/reflect-metadata).
1088
+
1089
+ ```typescript
1090
+ import 'reflect-metadata';
1091
+ import { Type, Property } from '@dipscope/type-manager';
1092
+
1093
+ @Type()
1094
+ export class User
1095
+ {
1096
+ @Property() public name: string;
1097
+ @Property() public createdAt: DateTime;
1098
+ }
1099
+ ```
1100
+
1101
+ And without [reflect-metadata](https://github.com/rbuckton/reflect-metadata).
1102
+
1103
+ ```typescript
1104
+ import { Type, Property } from '@dipscope/type-manager';
1105
+
1106
+ @Type()
1107
+ export class User
1108
+ {
1109
+ @Property(String) public name: string;
1110
+ @Property(DateTime) public createdAt: DateTime;
1111
+ }
1112
+ ```
1113
+
1114
+ ### Configuring usage of polymorphic types
1115
+
1116
+ 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.
1117
+
1118
+ ```typescript
1119
+ import { Type, Property } from '@dipscope/type-manager';
1120
+
1121
+ @Type()
1122
+ export abstract class Shape
1123
+ {
1124
+ @Property(String) public title: string;
1125
+ }
1126
+
1127
+ @Type()
1128
+ export class Rectangle extends Shape
1129
+ {
1130
+ @Property(Number) public width: number;
1131
+ @Property(Number) public height: number;
1132
+ }
1133
+
1134
+ @Type()
1135
+ export class Square extends Shape
1136
+ {
1137
+ @Property(Number) public width: number;
1138
+ }
1139
+
1140
+ @Type()
1141
+ export class Circle extends Shape
1142
+ {
1143
+ @Property(Number) public radius: number;
1144
+ }
1145
+ ```
1146
+
1147
+ Some other class declares a `shapes` property in it.
1148
+
1149
+ ```typescript
1150
+ import { Type, Property } from '@dipscope/type-manager';
1151
+
1152
+ @Type()
1153
+ export class Plot
1154
+ {
1155
+ @Property(Array, [Shape]) public shapes: Array<Shape>;
1156
+ }
1157
+ ```
1158
+
1159
+ 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.
1160
+
1161
+ ```json
1162
+ {
1163
+ "shapes": [
1164
+ {
1165
+ "title": "Cool rectangle",
1166
+ "width": 10,
1167
+ "height": 10
1168
+ },
1169
+ {
1170
+ "title": "Perfect square",
1171
+ "width": 10
1172
+ },
1173
+ {
1174
+ "title": "Simple circle",
1175
+ "radius": 6
1176
+ }
1177
+ ]
1178
+ }
1179
+ ```
1180
+
1181
+ 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?
1182
+
1183
+ 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.
1184
+
1185
+ It uses 2 special configurable type options:
1186
+
1187
+ * `Discriminator` which defines a property inside an object which should be used to define a type.
1188
+ * `Discriminant` which represents a certain `Discriminator` value.
1189
+
1190
+ This options have default values if you have not configured them explicitly.
1191
+
1192
+ * Default value of discriminator is a `$type`. During deserialization `TypeManager` expects such property to be present inside a polymorphic object.
1193
+ * Default value of discriminant is a `ClassName` which determined based on the type function.
1194
+
1195
+ For proper deserialization of polymorphic types you have to provide such information inside your JSON.
1196
+
1197
+ ```json
1198
+ {
1199
+ "shapes": [
1200
+ {
1201
+ "$type": "Rectangle",
1202
+ "title": "Cool rectangle",
1203
+ "width": 10,
1204
+ "height": 10
1205
+ },
1206
+ {
1207
+ "$type": "Square",
1208
+ "title": "Perfect square",
1209
+ "width": 10
1210
+ },
1211
+ {
1212
+ "$type": "Circle",
1213
+ "title": "Simple circle",
1214
+ "radius": 6
1215
+ }
1216
+ ]
1217
+ }
1218
+ ```
1219
+
1220
+ Now your JSON will be handled properly and you will get `Rectangle`, `Square` and `Circle` class instances in return.
1221
+
1222
+ 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.
1223
+
1224
+ ```json
1225
+ {
1226
+ "shapes": [
1227
+ {
1228
+ "$type": "Company.Api.Entities.Rectangle",
1229
+ "title": "Cool rectangle",
1230
+ "width": 10,
1231
+ "height": 10
1232
+ },
1233
+ {
1234
+ "$type": "Company.Api.Entities.Square",
1235
+ "title": "Perfect square",
1236
+ "width": 10
1237
+ },
1238
+ {
1239
+ "$type": "Company.Api.Entities.Circle",
1240
+ "title": "Simple circle",
1241
+ "radius": 6
1242
+ }
1243
+ ]
1244
+ }
1245
+ ```
1246
+
1247
+ To change `Discriminator` globally you have to use `TypeManager` configure method.
1248
+
1249
+ ```typescript
1250
+ import { TypeManagerOptions, TypeOptionsBase } from '@dipscope/type-manager';
1251
+
1252
+ const typeOptionsBase: TypeOptionsBase<any> = {
1253
+ discriminator: '$customType'
1254
+ };
1255
+
1256
+ const typeManagerOptions: TypeManagerOptions = {
1257
+ typeOptionsBase: typeOptionsBase
1258
+ };
1259
+
1260
+ TypeManager.configure(typeManagerOptions);
1261
+ ```
1262
+
1263
+ To change `Discriminant` you have to use per type configuration.
1264
+
1265
+ ```typescript
1266
+ import { TypeManagerOptions, TypeFn, TypeOptions, PropertyName, PropertyOptions } from '@dipscope/type-manager';
1267
+
1268
+ const rectangleOptions: TypeOptions<Rectangle> = {
1269
+ discriminant: 'Company.Api.Entities.Rectangle'
1270
+ };
1271
+
1272
+ const squareOptions: TypeOptions<Square> = {
1273
+ discriminant: 'Company.Api.Entities.Square'
1274
+ };
1275
+
1276
+ const circleOptions: TypeOptions<Circle> = {
1277
+ discriminant: 'Company.Api.Entities.Circle'
1278
+ };
1279
+
1280
+ const typeManagerOptions: TypeManagerOptions = {
1281
+ typeOptionsMap: new Map<TypeFn<any>, TypeOptions<any>>(
1282
+ [Rectangle, rectangleOptions],
1283
+ [Square, squareOptions],
1284
+ [Circle, circleOptions]
1285
+ )
1286
+ };
1287
+
1288
+ TypeManager.configure(typeManagerOptions);
1289
+ ```
1290
+
1291
+ As an alternative you can change `Discriminant` as the following using decorators.
1292
+
1293
+ ```typescript
1294
+ import { Type, Property } from '@dipscope/type-manager';
1295
+
1296
+ @Type({
1297
+ discriminant: 'Company.Api.Entities.Shape'
1298
+ })
1299
+ export abstract class Shape
1300
+ {
1301
+ @Property(String) public title: string;
1302
+ }
1303
+
1304
+ @Type({
1305
+ discriminant: 'Company.Api.Entities.Rectangle'
1306
+ })
1307
+ export class Rectangle extends Shape
1308
+ {
1309
+ @Property(Number) public width: number;
1310
+ @Property(Number) public height: number;
1311
+ }
1312
+
1313
+ @Type({
1314
+ discriminant: 'Company.Api.Entities.Square'
1315
+ })
1316
+ export class Square extends Shape
1317
+ {
1318
+ @Property(Number) public width: number;
1319
+ }
1320
+
1321
+ @Type({
1322
+ discriminant: 'Company.Api.Entities.Circle'
1323
+ })
1324
+ export class Circle extends Shape
1325
+ {
1326
+ @Property(Number) public radius: number;
1327
+ }
1328
+ ```
1329
+
1330
+ 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.
1331
+
1332
+ ```typescript
1333
+ import { TypeManagerOptions, TypeOptionsBase } from '@dipscope/type-manager';
1334
+
1335
+ const typeOptionsBase: TypeOptionsBase<any> = {
1336
+ preserveDiscriminator: true
1337
+ };
1338
+
1339
+ const typeManagerOptions: TypeManagerOptions = {
1340
+ typeOptionsBase: typeOptionsBase
1341
+ };
1342
+
1343
+ TypeManager.configure(typeManagerOptions);
1344
+ ```
1345
+
1346
+ With this option enabled discriminator will be present in output data.
1347
+
1348
+ ### Configuring naming convention
1349
+
1350
+ 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.
1351
+
1352
+ ```typescript
1353
+ import { Type, Property } from '@dipscope/type-manager';
1354
+
1355
+ @Type()
1356
+ export class User
1357
+ {
1358
+ @Property(String) public name: string;
1359
+ @Property(Number) public loginCount: number;
1360
+ @Property(DateTime) public createdAt: DateTime;
1361
+ }
1362
+ ```
1363
+
1364
+ Our JSON should match the same naming convention to work properly.
1365
+
1366
+ ```json
1367
+ [
1368
+ {
1369
+ "name": "Dmitry",
1370
+ "loginCount": 10,
1371
+ "createdAt": "2021-02-22T20:15:00.000Z"
1372
+ },
1373
+ {
1374
+ "name": "Alex",
1375
+ "loginCount": 25,
1376
+ "createdAt": "2021-02-22T21:15:00.000Z"
1377
+ },
1378
+ {
1379
+ "name": "Anna",
1380
+ "loginCount": 3,
1381
+ "createdAt": "2021-02-22T21:15:23.000Z"
1382
+ }
1383
+ ]
1384
+ ```
1385
+
1386
+ But what to do if we don't control the JSON naming convention so it comes to us in a snake case?
1387
+
1388
+ ```json
1389
+ [
1390
+ {
1391
+ "name": "Dmitry",
1392
+ "login_count": 10,
1393
+ "created_at": "2021-02-22T20:15:00.000Z"
1394
+ },
1395
+ {
1396
+ "name": "Alex",
1397
+ "login_count": 25,
1398
+ "created_at": "2021-02-22T21:15:00.000Z"
1399
+ },
1400
+ {
1401
+ "name": "Anna",
1402
+ "login_count": 3,
1403
+ "created_at": "2021-02-22T21:15:23.000Z"
1404
+ }
1405
+ ]
1406
+ ```
1407
+
1408
+ We can still parse such a JSON by specifying an alias for each property but this will become a pain in a while.
1409
+
1410
+ ```typescript
1411
+ import { Type, Property } from '@dipscope/type-manager';
1412
+
1413
+ @Type()
1414
+ export class User
1415
+ {
1416
+ @Property(String) public name: string;
1417
+ @Property(Number, { alias: 'login_count' }) public loginCount: number;
1418
+ @Property(DateTime, { alias: 'created_at' }) public createdAt: DateTime;
1419
+ }
1420
+ ```
1421
+
1422
+ `TypeManager` supports several naming conventions we can use to avoid dealing with aliases.
1423
+
1424
+ * Camel case [camelCase];
1425
+ * Flat case [flatcase];
1426
+ * Flat upper case [FLATCASE];
1427
+ * Kebab case [kebab-kase];
1428
+ * Kebab upper case [KEBAB-CASE];
1429
+ * Pascal case [PascalCase];
1430
+ * Snake case [snake_case];
1431
+ * Snake upper case [SNAKE_CASE];
1432
+
1433
+ To set one we have to configure global options.
1434
+
1435
+ ```typescript
1436
+ import { TypeManagerOptions, TypeOptionsBase, SnakeCaseNamingConvention } from '@dipscope/type-manager';
1437
+
1438
+ const typeOptionsBase: TypeOptionsBase<any> = {
1439
+ namingConvention: new SnakeCaseNamingConvention()
1440
+ };
1441
+
1442
+ const typeManagerOptions: TypeManagerOptions = {
1443
+ typeOptionsBase: typeOptionsBase
1444
+ };
1445
+
1446
+ TypeManager.configure(typeManagerOptions);
1447
+ ```
1448
+
1449
+ 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.
1450
+
1451
+ ### Configuring reference handler
1452
+
1453
+ Reference handler defines how references to the same objects including a circular one should be treated. We have several reference handlers: circular, json path and plain. Each of them can be used globally or per type.
1454
+
1455
+ * Circular reference handler preserves object references without making any special changes;
1456
+ * Json path reference handler preserves object references using JSONPath notation;
1457
+ * Plain reference handler preserves object references excluding a circular one. When circular reference is detected it will be set to undefined;
1458
+
1459
+ There is nothing better to show the difference than code. For example we have two classes which reference each other:
1460
+
1461
+ ```typescript
1462
+ import { Type, Property } from '@dipscope/type-manager';
1463
+
1464
+ @Type()
1465
+ class User
1466
+ {
1467
+ @Property(() => Company) public company: Company;
1468
+ }
1469
+
1470
+ @Type()
1471
+ class Company
1472
+ {
1473
+ @Property(() => User) public user: User;
1474
+ }
1475
+ ```
1476
+
1477
+ Somewhere in code you have such a logic:
1478
+
1479
+ ```typescript
1480
+ import { TypeManager } from '@dipscope/type-manager';
1481
+
1482
+ const user = new User();
1483
+ const company = new Company();
1484
+
1485
+ user.company = company;
1486
+ company.user = user;
1487
+
1488
+ const result = TypeManager.serialize(User, user);
1489
+ ```
1490
+
1491
+ Here are results returned by different reference handlers:
1492
+
1493
+ ```typescript
1494
+ // Circular reference handler...
1495
+ { company: { user: result } };
1496
+
1497
+ // Json path reference handler...
1498
+ { company: { user: { $ref: '$' } } };
1499
+
1500
+ // Plain reference handler...
1501
+ { company: { user: undefined };
1502
+ ```
1503
+
1504
+ As you can see `CircularReferenceHandler` 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 `JsonPathReferenceHandler` 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 `PlainReferenceHandler`. To change default reference handler you have to use `TypeManager` configure methods.
1505
+
1506
+ ```typescript
1507
+ import { TypeManagerOptions, TypeOptionsBase, JsonPathReferenceHandler } from '@dipscope/type-manager';
1508
+
1509
+ const typeOptionsBase: TypeOptionsBase<any> = {
1510
+ referenceHandler: new JsonPathReferenceHandler()
1511
+ };
1512
+
1513
+ const typeManagerOptions: TypeManagerOptions = {
1514
+ typeOptionsBase: typeOptionsBase
1515
+ };
1516
+
1517
+ TypeManager.configure(typeManagerOptions);
1518
+ ```
1519
+
1520
+ With such configuration any reference will be handled using JSONPath so you are completely free from errors during conversion to a string.
1521
+
1522
+ ## Advanced usage
1523
+
1524
+ Our goal is to cover as much use cases as possible without making you to write additional code but there always be an application specific case. With that in mind we allow you to customize and extend each part of our pipeline.
1525
+
1526
+ ### Defining custom data
1527
+
1528
+ You can attach you custom metadata to our decorators using `customOptions` option available on `Type` and `Property`.
1529
+
1530
+ ```typescript
1531
+ import { Type, Property, CustomKey } from '@dipscope/type-manager';
1532
+
1533
+ const rankKey = new CustomKey<number>('rank');
1534
+ const priorityKey = new CustomKey<number>('priority');
1535
+
1536
+ @Type({
1537
+ customOptions: [
1538
+ [rankKey, 1]
1539
+ ]
1540
+ })
1541
+ class User
1542
+ {
1543
+ @Property(String, { customOptions: [[priorityKey, 10]] }) public name: string;
1544
+ }
1545
+ ```
1546
+
1547
+ 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`.
1548
+
1549
+ ```typescript
1550
+ import { TypeManager } from '@dipscope/type-manager';
1551
+
1552
+ const userMetadata = TypeManager.extractTypeMetadata(User);
1553
+ const typeCustomContext = userMetadata.customContext;
1554
+ const rank = customContext.get(rankKey);
1555
+
1556
+ // Do something with type custom data...
1557
+
1558
+ for (const propertyMetadata of userMetadata.propertyMetadataMap.values())
1559
+ {
1560
+ const propertyCustomContext = propertyMetadata.customContext;
1561
+ const priority = propertyCustomContext.get(priorityKey);
1562
+
1563
+ // Do something with property custom data...
1564
+ }
1565
+ ```
1566
+
1567
+ ### Defining custom serializer
1568
+
1569
+ 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.
1570
+
1571
+ ```typescript
1572
+ import { Serializer, TypeLike, SerializerContext } from '@dipscope/type-manager';
1573
+ import { Fn } from '@app/module';
1574
+
1575
+ export class DateTimeSerializer implements Serializer<DateTime>
1576
+ {
1577
+ public serialize(x: TypeLike<DateTime>, serializerContext: SerializerContext<DateTime>): TypeLike<string>
1578
+ {
1579
+ if (Fn.isUndefined(x))
1580
+ {
1581
+ return serializerContext.serializedDefaultValue;
1582
+ }
1583
+
1584
+ if (Fn.isNull(x))
1585
+ {
1586
+ return x;
1587
+ }
1588
+
1589
+ if (Fn.isDateTime(x))
1590
+ {
1591
+ return x.toIsoString();
1592
+ }
1593
+
1594
+ if (serializerContext.log.errorEnabled)
1595
+ {
1596
+ serializerContext.log.error(`${serializerContext.path}: cannot serialize value as date time.`, x);
1597
+ }
1598
+
1599
+ return undefined;
1600
+ }
1601
+
1602
+ public deserialize(x: TypeLike<string>, serializerContext: SerializerContext<DateTime>): TypeLike<DateTime>
1603
+ {
1604
+ if (Fn.isUndefined(x))
1605
+ {
1606
+ return serializerContext.deserializedDefaultValue;
1607
+ }
1608
+
1609
+ if (Fn.isNull(x))
1610
+ {
1611
+ return x;
1612
+ }
1613
+
1614
+ if (Fn.isString(x))
1615
+ {
1616
+ return DateTime.fromIsoString(x);
1617
+ }
1618
+
1619
+ if (serializerContext.log.errorEnabled)
1620
+ {
1621
+ serializerContext.log.error(`${serializerContext.path}: cannot deserialize value as date time.`, x);
1622
+ }
1623
+
1624
+ return undefined;
1625
+ }
1626
+ }
1627
+ ```
1628
+
1629
+ 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.
1630
+
1631
+ 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.
1632
+
1633
+ When you are finished with definitions there are two possible ways to register a serializer. You can use decorators.
1634
+
1635
+ ```typescript
1636
+ import { Type, Serializer } from '@dipscope/type-manager';
1637
+
1638
+ @Type({
1639
+ serializer: new DateTimeSerializer()
1640
+ })
1641
+ export class DateTime
1642
+ {
1643
+ ...
1644
+ }
1645
+ ```
1646
+
1647
+ Or declarative configuration.
1648
+
1649
+ ```typescript
1650
+ import { TypeManager, TypeConfiguration, TypeMetadata } from '@dipscope/type-manager';
1651
+
1652
+ export class DateTimeConfiguration implements TypeConfiguration<DateTime>
1653
+ {
1654
+ public configure(typeMetadata: TypeMetadata<DateTime>): void
1655
+ {
1656
+ typeMetadata.hasAlias('DateTime')
1657
+ .hasSerializer(new DateTimeSerializer());
1658
+
1659
+ return;
1660
+ }
1661
+ }
1662
+
1663
+ TypeManager.applyTypeConfiguration(DateTime, new DateTimeConfiguration());
1664
+ ```
1665
+
1666
+ With declarative configuration it is possible to override built in serializers if it's behaviour not suitable for your use cases. Also you can register serializers for types we not yet cover.
1667
+
1668
+ ### Defining custom injector
1669
+
1670
+ In modern world we are always use some kind of framework to build our application. It is definitely already have a configured dependency injection container so let's configure `TypeManager` for using it instead of build in one. You have to implement `Injector` interface with only one method. Here how it may look like in `Angular`.
1671
+
1672
+ ```typescript
1673
+ import { Injector, TypeMetadata } from '@dipscope/type-manager';
1674
+ import { Injector as AngularInjector } from '@angular/core';
1675
+
1676
+ export class CustomInjector implements Injector
1677
+ {
1678
+ private readonly angularInjector: AngularInjector;
1679
+
1680
+ public constructor(angularInjector: AngularInjector)
1681
+ {
1682
+ this.angularInjector = angularInjector;
1683
+
1684
+ return;
1685
+ }
1686
+
1687
+ public get<TType>(typeMetadata: TypeMetadata<TType>): TType | undefined
1688
+ {
1689
+ return this.angularInjector.get(typeMetadata.typeFn);
1690
+ }
1691
+ }
1692
+ ```
1693
+
1694
+ In general you have to get framework injector or create it manually. Then call method to get and instance by type when one is requested. Implementations can differ but we hope idea is clear. When you are finished with definitions you have to register custom injector for a `TypeManager`.
1695
+
1696
+ ```typescript
1697
+ import { TypeManager } from '@dipscope/type-manager';
1698
+ import { Injector as AngularInjector } from '@angular/core';
1699
+
1700
+ const angularInjector: AngularInjector = ...; // Get framework injector in core module for example.
1701
+
1702
+ TypeManager.applyTypeOptionsBase({
1703
+ injector: new CustomInjector(angularInjector)
1704
+ });
1705
+ ```
1706
+
1707
+ Now types will be resolved using framework injector.
1708
+
1709
+ ### Defining custom factory
1710
+
1711
+ When you want to apply additional logic to how types are constructed you can specify custom factory globally or per type. Let's say you want to init some properties based on your custom data specified for a type. You can do this by extending default `TypeFactory`.
1712
+
1713
+ ```typescript
1714
+ import { TypeContext, Injector, TypeFactory } from '@dipscope/type-manager';
1715
+
1716
+ export class CustomTypeFactory extends TypeFactory
1717
+ {
1718
+ public build<TType>(typeContext: TypeContext<TType>, injector: Injector): TType
1719
+ {
1720
+ // Build any type.
1721
+ const type = super.build(typeContext, injector) as any;
1722
+
1723
+ // Resolve custom data.
1724
+ const typeMetadata = typeContext.typeMetadata;
1725
+ const customContext = typeMetadata.customContext;
1726
+
1727
+ // Get rank based on key.
1728
+ const rank = customContext.get(rankKey);
1729
+
1730
+ // Process custom data...
1731
+
1732
+ return type;
1733
+ }
1734
+ }
1735
+ ```
1736
+
1737
+ When you are finished with definitions there are two possible ways to register a factory. You can use decorators.
1738
+
1739
+ ```typescript
1740
+ import { Type, Factory } from '@dipscope/type-manager';
1741
+
1742
+ @Type({
1743
+ customOptions: [[rankKey, 1]]
1744
+ factory: new CustomTypeFactory()
1745
+ })
1746
+ export class User
1747
+ {
1748
+ ...
1749
+ }
1750
+ ```
1751
+
1752
+ Or declarative configuration.
1753
+
1754
+ ```typescript
1755
+ import { TypeManager } from '@dipscope/type-manager';
1756
+
1757
+ // Overriding only for user type.
1758
+ TypeManager.applyTypeOptions(User, {
1759
+ customOptions: [[rankKey, 1]],
1760
+ factory: new CustomTypeFactory()
1761
+ });
1762
+
1763
+ // Overriding for any type.
1764
+ TypeManager.applyTypeOptionsBase({
1765
+ factory: new CustomTypeFactory()
1766
+ });
1767
+ ```
1768
+
1769
+ ### Defining custom naming convention
1770
+
1771
+ To define custom naming convention you have to implement `NamingConvention` interface with only one `convert` method. Here is an example implementation of camel case naming convention.
1772
+
1773
+ ```typescript
1774
+ import { NamingConvention, getWords } from '@dipscope/type-manager';
1775
+
1776
+ export class CamelCaseNamingConvention implements NamingConvention
1777
+ {
1778
+ public convert(name: string): string
1779
+ {
1780
+ return getWords(name).reduce(this.reduce, '');
1781
+ }
1782
+
1783
+ private reduce(result: string, word: string, index: number): string
1784
+ {
1785
+ if (word.length === 0)
1786
+ {
1787
+ return result;
1788
+ }
1789
+
1790
+ const wordLowerCased = word.toLowerCase();
1791
+
1792
+ if (index === 0)
1793
+ {
1794
+ return wordLowerCased;
1795
+ }
1796
+
1797
+ return `${result}${wordLowerCased[0].toUpperCase()}${wordLowerCased.slice(1)}`;
1798
+ }
1799
+ }
1800
+ ```
1801
+
1802
+ Public `convert` method receives a property name as it declared in a class. You have to call internal `getWords` function on it which will split property name into array of the words. In the `reduce` function you can combine this words to whatever string you want. When you are finished with definitions you have to register custom naming convention for a `TypeManager`.
1803
+
1804
+ ```typescript
1805
+ import { TypeManager } from '@dipscope/type-manager';
1806
+
1807
+ TypeManager.applyTypeOptionsBase({
1808
+ namingConvention: new CamelCaseNamingConvention()
1809
+ });
1810
+ ```
1811
+
1812
+ Now property names will be resolved using your custom naming convention.
1813
+
1814
+ ## Use cases
1815
+
1816
+ 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.
1817
+
1818
+ ### Built in serializers
1819
+
1820
+ Here is a list of types with built in serializers.
1821
+
1822
+ * Any;
1823
+ * Array;
1824
+ * ArrayBuffer;
1825
+ * Boolean;
1826
+ * DataView;
1827
+ * Date;
1828
+ * Float32Array;
1829
+ * Float64Array;
1830
+ * Int8Array;
1831
+ * Int16Array;
1832
+ * Int32Array;
1833
+ * Map;
1834
+ * Number;
1835
+ * Set;
1836
+ * String;
1837
+ * Uint8Array;
1838
+ * Uint8ClampedArray;
1839
+ * Uint16Array;
1840
+ * Uint32Array;
1841
+ * Unknown;
1842
+
1843
+ 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.
1844
+
1845
+ ### Circular object references
1846
+
1847
+ 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.
1848
+
1849
+ ### Dependency injection and immutable types
1850
+
1851
+ Follow the steps described in [inject decorator](#inject-decorator) section. To integrate our injection system with you yours check [defining custom injector](#defining-custom-injector) section.
1852
+
1853
+ ### Different case usage in class and JSON
1854
+
1855
+ 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.
1856
+
1857
+ ### Enum types
1858
+
1859
+ 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).
1860
+
1861
+ ### Generic types
1862
+
1863
+ Generic types are supported and you can define them. Read [property decorator](#property-decorator) section for more info.
1864
+
1865
+ ### Integration with Angular
1866
+
1867
+ With `Angular` you do not need to install [reflect-metadata](https://github.com/rbuckton/reflect-metadata) as it is already included in `core-js`. However, you still need to instruct CLI to include it in the build. This can be achieved simply by adding `import 'reflect-metadata';` to you `main.ts` file.
1868
+
1869
+ 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.
1870
+
1871
+ ### Polymorphic types
1872
+
1873
+ 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.
1874
+
1875
+ ## Versioning
1876
+
1877
+ We use [SemVer](http://semver.org/) for versioning. For the versions available, see the versions section on [NPM project page](https://www.npmjs.com/package/@dipscope/type-manager).
1878
+
1879
+ See information about breaking changes, release notes and migration steps between versions in [CHANGELOG.md](https://github.com/dipscope/TypeManager.TS/blob/master/CHANGELOG.md) file.
1880
+
1881
+ ## Contributing
1882
+
1883
+ Please read [CONTRIBUTING.md](https://github.com/dipscope/TypeManager.TS/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
1884
+
1885
+ ## Authors
1886
+
1887
+ * **Dmitry Pimonov** - *Initial work* - [dpimonov](https://github.com/dpimonov)
1888
+
1889
+ See also the list of [contributors](https://github.com/dipscope/TypeManager.TS/contributors) who participated in this project.
1890
+
1891
+ ## Notes
1892
+
1893
+ Thanks for checking this package.
1894
+
1895
+ Feel free to create an issue if you find any mistakes in documentation or have any improvements in mind.
1896
+
1897
+ We wish you good luck and happy coding!
1898
+
1899
+ ## License
1900
+
1901
+ This project is licensed under the Apache 2.0 License - see the [LICENSE.md](https://github.com/dipscope/TypeManager.TS/blob/master/LICENSE.md) file for details.