1 | # class-transformer
|
2 |
|
3 | [![Build Status](https://travis-ci.org/typestack/class-transformer.svg?branch=master)](https://travis-ci.org/typestack/class-transformer)
|
4 | [![codecov](https://codecov.io/gh/typestack/class-transformer/branch/master/graph/badge.svg)](https://codecov.io/gh/typestack/class-transformer)
|
5 | [![npm version](https://badge.fury.io/js/class-transformer.svg)](https://badge.fury.io/js/class-transformer)
|
6 | [![Dependency Status](https://david-dm.org/typestack/class-transformer.svg)](https://david-dm.org/typestack/class-transformer)
|
7 | [![Join the chat at https://gitter.im/typestack/class-transformer](https://badges.gitter.im/typestack/class-transformer.svg)](https://gitter.im/typestack/class-transformer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
8 |
|
9 | Its ES6 and Typescript era. Nowadays you are working with classes and constructor objects more then ever.
|
10 | Class-transformer allows you to transform plain object to some instance of class and versa.
|
11 | Also it allows to serialize / deserialize object based on criteria.
|
12 | This tool is super useful on both frontend and backend.
|
13 |
|
14 | Example how to use with angular 2 in [plunker](http://plnkr.co/edit/Mja1ZYAjVySWASMHVB9R).
|
15 | Source code is available [here](https://github.com/pleerock/class-transformer-demo).
|
16 |
|
17 | ## What is class-transformer
|
18 |
|
19 | In JavaScript there are two types of objects:
|
20 |
|
21 | * plain (literal) objects
|
22 | * class (constructor) objects
|
23 |
|
24 | Plain objects are objects that are instances of `Object` class.
|
25 | Sometimes they are called **literal** objects, when created via `{}` notation.
|
26 | Class objects are instances of classes with own defined constructor, properties and methods.
|
27 | Usually you define them via `class` notation.
|
28 |
|
29 | So, what is the problem?
|
30 |
|
31 | Sometimes you want to transform plain javascript object to the ES6 **classes** you have.
|
32 | For example, if you are loading a json from your backend, some api or from a json file,
|
33 | and after you `JSON.parse` it you have a plain javascript object, not instance of class you have.
|
34 |
|
35 | For example you have a list of users in your `users.json` that you are loading:
|
36 |
|
37 | ```json
|
38 | [{
|
39 | "id": 1,
|
40 | "firstName": "Johny",
|
41 | "lastName": "Cage",
|
42 | "age": 27
|
43 | },
|
44 | {
|
45 | "id": 2,
|
46 | "firstName": "Ismoil",
|
47 | "lastName": "Somoni",
|
48 | "age": 50
|
49 | },
|
50 | {
|
51 | "id": 3,
|
52 | "firstName": "Luke",
|
53 | "lastName": "Dacascos",
|
54 | "age": 12
|
55 | }]
|
56 | ```
|
57 | And you have a `User` class:
|
58 |
|
59 | ```javascript
|
60 | export class User {
|
61 | id: number;
|
62 | firstName: string;
|
63 | lastName: string;
|
64 | age: number;
|
65 |
|
66 | getName() {
|
67 | return this.firstName + " " + this.lastName;
|
68 | }
|
69 |
|
70 | isAdult() {
|
71 | return this.age > 36 && this.age < 60;
|
72 | }
|
73 | }
|
74 | ```
|
75 |
|
76 | You are assuming that you are downloading users of type `User` from `users.json` file and may want to write
|
77 | following code:
|
78 |
|
79 | ```javascript
|
80 | fetch("users.json").then((users: User[]) => {
|
81 | // you can use users here, and type hinting also will be available to you,
|
82 | // but users are not actually instances of User class
|
83 | // this means that you can't use methods of User class
|
84 | });
|
85 | ```
|
86 |
|
87 | In this code you can use `users[0].id`, you can also use `users[0].firstName` and `users[0].lastName`.
|
88 | However you cannot use `users[0].getName()` or `users[0].isAdult()` because "users" actually is
|
89 | array of plain javascript objects, not instances of User object.
|
90 | You actually lied to compiler when you said that its `users: User[]`.
|
91 |
|
92 | So what to do? How to make a `users` array of instances of `User` objects instead of plain javascript objects?
|
93 | Solution is to create new instances of User object and manually copy all properties to new objects.
|
94 | But things may go wrong very fast once you have a more complex object hierarchy.
|
95 |
|
96 | Alternatives? Yes, you can use class-transformer. Purpose of this library is to help you to map you plain javascript
|
97 | objects to the instances of classes you have.
|
98 |
|
99 | This library also great for models exposed in your APIs,
|
100 | because it provides a great tooling to control what your models are exposing in your API.
|
101 | Here is example how it will look like:
|
102 |
|
103 | ```javascript
|
104 | fetch("users.json").then((users: Object[]) => {
|
105 | const realUsers = plainToClass(User, users);
|
106 | // now each user in realUsers is instance of User class
|
107 | });
|
108 | ```
|
109 |
|
110 | Now you can use `users[0].getName()` and `users[0].isAdult()` methods.
|
111 |
|
112 | ## Installation
|
113 |
|
114 | ### Node.js
|
115 |
|
116 | 1. Install module:
|
117 |
|
118 | `npm install class-transformer --save`
|
119 |
|
120 | 2. `reflect-metadata` shim is required, install it too:
|
121 |
|
122 | `npm install reflect-metadata --save`
|
123 |
|
124 | and make sure to import it in a global place, like app.ts:
|
125 |
|
126 | ```javascript
|
127 | import "reflect-metadata";
|
128 | ```
|
129 |
|
130 | 3. ES6 features are used, if you are using old version of node.js you may need to install es6-shim:
|
131 |
|
132 | `npm install es6-shim --save`
|
133 |
|
134 | and import it in a global place like app.ts:
|
135 |
|
136 | ```javascript
|
137 | import "es6-shim";
|
138 | ```
|
139 |
|
140 | ### Browser
|
141 |
|
142 | 1. Install module:
|
143 |
|
144 | `npm install class-transformer --save`
|
145 |
|
146 | 2. `reflect-metadata` shim is required, install it too:
|
147 |
|
148 | `npm install reflect-metadata --save`
|
149 |
|
150 | add `<script>` to reflect-metadata in the head of your `index.html`:
|
151 |
|
152 | ```html
|
153 | <html>
|
154 | <head>
|
155 | <!-- ... -->
|
156 | <script src="node_modules/reflect-metadata/Reflect.js"></script>
|
157 | </head>
|
158 | <!-- ... -->
|
159 | </html>
|
160 | ```
|
161 |
|
162 | If you are using angular 2 you should already have this shim installed.
|
163 |
|
164 | 3. If you are using system.js you may want to add this into `map` and `package` config:
|
165 |
|
166 | ```json
|
167 | {
|
168 | "map": {
|
169 | "class-transformer": "node_modules/class-transformer"
|
170 | },
|
171 | "packages": {
|
172 | "class-transformer": { "main": "index.js", "defaultExtension": "js" }
|
173 | }
|
174 | }
|
175 | ```
|
176 |
|
177 | ## Methods
|
178 |
|
179 | #### plainToClass
|
180 |
|
181 | This method transforms a plain javascript object to instance of specific class.
|
182 |
|
183 | ```javascript
|
184 | import {plainToClass} from "class-transformer";
|
185 |
|
186 | let users = plainToClass(User, userJson); // to convert user plain object a single user. also supports arrays
|
187 | ```
|
188 |
|
189 | #### plainToClassFromExist
|
190 |
|
191 | This method transforms a plain object into a instance using a already filled Object which is a instance from the target class.
|
192 |
|
193 | ```javascript
|
194 | const defaultUser = new User();
|
195 | defaultUser.role = 'user';
|
196 |
|
197 | let mixedUser = plainToClassFromExist(defaultUser, user); // mixed user should have the value role = user when no value is set otherwise.
|
198 | ```
|
199 |
|
200 | #### classToPlain
|
201 |
|
202 | This method transforms your class object back to plain javascript object, that can be `JSON.stringify` later.
|
203 |
|
204 | ```javascript
|
205 | import {classToPlain} from "class-transformer";
|
206 | let photo = classToPlain(photo);
|
207 | ```
|
208 |
|
209 | #### classToClass
|
210 |
|
211 | This method transforms your class object into new instance of the class object.
|
212 | This maybe treated as deep clone of your objects.
|
213 |
|
214 | ```javascript
|
215 | import {classToClass} from "class-transformer";
|
216 | let photo = classToClass(photo);
|
217 | ```
|
218 |
|
219 | You can also use a `ignoreDecorators` option in transformation options to ignore all decorators you classes is using.
|
220 |
|
221 | #### serialize
|
222 |
|
223 | You can serialize your model right to the json using `serialize` method:
|
224 |
|
225 | ```javascript
|
226 | import {serialize} from "class-transformer";
|
227 | let photo = serialize(photo);
|
228 | ```
|
229 |
|
230 | `serialize` works with both arrays and non-arrays.
|
231 |
|
232 | #### deserialize and deserializeArray
|
233 |
|
234 | You can deserialize your model to from a json using `deserialize` method:
|
235 |
|
236 | ```javascript
|
237 | import {deserialize} from "class-transformer";
|
238 | let photo = deserialize(Photo, photo);
|
239 | ```
|
240 |
|
241 | To make deserialization to work with arrays use `deserializeArray` method:
|
242 |
|
243 | ```javascript
|
244 | import {deserializeArray} from "class-transformer";
|
245 | let photos = deserializeArray(Photo, photos);
|
246 | ```
|
247 |
|
248 | ## Enforcing type-safe instance
|
249 |
|
250 | The default behaviour of the `plainToClass` method is to set *all* properties from the plain object,
|
251 | even those which are not specified in the class.
|
252 |
|
253 | ```javascript
|
254 | import {plainToClass} from "class-transformer";
|
255 |
|
256 | class User {
|
257 | id: number
|
258 | firstName: string
|
259 | lastName: string
|
260 | }
|
261 |
|
262 | const fromPlainUser = {
|
263 | unkownProp: 'hello there',
|
264 | firstName: 'Umed',
|
265 | lastName: 'Khudoiberdiev',
|
266 | }
|
267 |
|
268 | console.log(plainToClass(User, fromPlainUser))
|
269 |
|
270 | // User {
|
271 | // unkownProp: 'hello there',
|
272 | // firstName: 'Umed',
|
273 | // lastName: 'Khudoiberdiev',
|
274 | // }
|
275 | ```
|
276 |
|
277 | If this behaviour does not suit your needs, you can use the `excludeExtraneousValues` option
|
278 | in the `plainToClass` method while *exposing all your class properties* as a requirement.
|
279 |
|
280 | ```javascript
|
281 | import {Expose, plainToClass} from "class-transformer";
|
282 |
|
283 | class User {
|
284 | @Expose() id: number;
|
285 | @Expose() firstName: string;
|
286 | @Expose() lastName: string;
|
287 | }
|
288 |
|
289 | const fromPlainUser = {
|
290 | unkownProp: 'hello there',
|
291 | firstName: 'Umed',
|
292 | lastName: 'Khudoiberdiev',
|
293 | }
|
294 |
|
295 | console.log(plainToClass(User, fromPlainUser, { excludeExtraneousValues: true }))
|
296 |
|
297 | // User {
|
298 | // id: undefined,
|
299 | // firstName: 'Umed',
|
300 | // lastName: 'Khudoiberdiev'
|
301 | // }
|
302 | ```
|
303 |
|
304 | ## Working with nested objects
|
305 |
|
306 | When you are trying to transform objects that have nested objects,
|
307 | its required to known what type of object you are trying to transform.
|
308 | Since Typescript does not have good reflection abilities yet,
|
309 | we should implicitly specify what type of object each property contain.
|
310 | This is done using `@Type` decorator.
|
311 |
|
312 | Lets say we have an album with photos.
|
313 | And we are trying to convert album plain object to class object:
|
314 |
|
315 | ```javascript
|
316 | import {Type, plainToClass} from "class-transformer";
|
317 |
|
318 | export class Album {
|
319 |
|
320 | id: number;
|
321 |
|
322 | name: string;
|
323 |
|
324 | @Type(() => Photo)
|
325 | photos: Photo[];
|
326 | }
|
327 |
|
328 | export class Photo {
|
329 | id: number;
|
330 | filename: string;
|
331 | }
|
332 |
|
333 | let album = plainToClass(Album, albumJson);
|
334 | // now album is Album object with Photo objects inside
|
335 | ```
|
336 |
|
337 | ### Providing more than one type option
|
338 |
|
339 | In case the nested object can be of different types, you can provide an additional options object,
|
340 | that specifies a discriminator. The discriminator option must define a `property` that holds the sub
|
341 | type name for the object and the possible `subTypes`, the nested object can converted to. A sub type
|
342 | has a `value`, that holds the constructor of the Type and the `name`, that can match with the `property`
|
343 | of the discriminator.
|
344 |
|
345 | Lets say we have an album that has a top photo. But this photo can be of certain different types.
|
346 | And we are trying to convert album plain object to class object. The plain object input has to define
|
347 | the additional property `__type`. This property is removed during transformation by default:
|
348 |
|
349 | **JSON input**:
|
350 | ```json
|
351 | {
|
352 | "id": 1,
|
353 | "name": "foo",
|
354 | "topPhoto": {
|
355 | "id": 9,
|
356 | "filename": "cool_wale.jpg",
|
357 | "depth": 1245,
|
358 | "__type": "underwater"
|
359 | }
|
360 | }
|
361 | ```
|
362 |
|
363 | ```javascript
|
364 | import {Type, plainToClass} from "class-transformer";
|
365 |
|
366 | export abstract class Photo {
|
367 | id: number;
|
368 | filename: string;
|
369 | }
|
370 |
|
371 | export class Landscape extends Photo {
|
372 | panorama: boolean;
|
373 | }
|
374 |
|
375 | export class Portrait extends Photo {
|
376 | person: Person;
|
377 | }
|
378 |
|
379 | export class UnderWater extends Photo {
|
380 | depth: number;
|
381 | }
|
382 |
|
383 | export class Album {
|
384 |
|
385 | id: number;
|
386 | name: string;
|
387 |
|
388 | @Type(() => Photo, {
|
389 | discriminator: {
|
390 | property: "__type",
|
391 | subTypes: [
|
392 | { value: Landscape, name: "landscape" },
|
393 | { value: Portrait, name: "portrait" },
|
394 | { value: UnderWater, name: "underwater" }
|
395 | ]
|
396 | }
|
397 | })
|
398 | topPhoto: Landscape | Portrait | UnderWater;
|
399 |
|
400 | }
|
401 |
|
402 | let album = plainToClass(Album, albumJson);
|
403 | // now album is Album object with a UnderWater object without `__type` property.
|
404 | ```
|
405 |
|
406 | Hint: The same applies for arrays with different sub types. Moreover you can specify `keepDiscriminatorProperty: true`
|
407 | in the options to keep the discriminator property also inside your resulting class.
|
408 |
|
409 | ## Exposing getters and method return values
|
410 |
|
411 | You can expose what your getter or method return by setting a `@Expose()` decorator to those getters or methods:
|
412 |
|
413 | ```javascript
|
414 | import {Expose} from "class-transformer";
|
415 |
|
416 | export class User {
|
417 |
|
418 | id: number;
|
419 | firstName: string;
|
420 | lastName: string;
|
421 | password: string;
|
422 |
|
423 | @Expose()
|
424 | get name() {
|
425 | return this.firstName + " " + this.lastName;
|
426 | }
|
427 |
|
428 | @Expose()
|
429 | getFullName() {
|
430 | return this.firstName + " " + this.lastName;
|
431 | }
|
432 | }
|
433 | ```
|
434 |
|
435 | ## Exposing properties with different names
|
436 |
|
437 | If you want to expose some of properties with a different name,
|
438 | you can do it by specifying a `name` option to `@Expose` decorator:
|
439 |
|
440 | ```javascript
|
441 | import {Expose} from "class-transformer";
|
442 |
|
443 | export class User {
|
444 |
|
445 | @Expose({ name: "uid" })
|
446 | id: number;
|
447 |
|
448 | firstName: string;
|
449 |
|
450 | lastName: string;
|
451 |
|
452 | @Expose({ name: "secretKey" })
|
453 | password: string;
|
454 |
|
455 | @Expose({ name: "fullName" })
|
456 | getFullName() {
|
457 | return this.firstName + " " + this.lastName;
|
458 | }
|
459 | }
|
460 | ```
|
461 |
|
462 | ## Skipping specific properties
|
463 |
|
464 | Sometimes you want to skip some properties during transformation.
|
465 | This can be done using `@Exclude` decorator:
|
466 |
|
467 | ```javascript
|
468 | import {Exclude} from "class-transformer";
|
469 |
|
470 | export class User {
|
471 |
|
472 | id: number;
|
473 |
|
474 | email: string;
|
475 |
|
476 | @Exclude()
|
477 | password: string;
|
478 | }
|
479 | ```
|
480 |
|
481 | Now when you transform a User, `password` property will be skipped and not be included in the transformed result.
|
482 |
|
483 | ## Skipping depend of operation
|
484 |
|
485 | You can control on what operation you will exclude a property. Use `toClassOnly` or `toPlainOnly` options:
|
486 |
|
487 | ```javascript
|
488 | import {Exclude} from "class-transformer";
|
489 |
|
490 | export class User {
|
491 |
|
492 | id: number;
|
493 |
|
494 | email: string;
|
495 |
|
496 | @Exclude({ toPlainOnly: true })
|
497 | password: string;
|
498 | }
|
499 | ```
|
500 |
|
501 | Now `password` property will be excluded only during `classToPlain` operation. Oppositely, use `toClassOnly` option.
|
502 |
|
503 | ## Skipping all properties of the class
|
504 |
|
505 | You can skip all properties of the class, and expose only those are needed explicitly:
|
506 |
|
507 | ```javascript
|
508 | import {Exclude, Expose} from "class-transformer";
|
509 |
|
510 | @Exclude()
|
511 | export class User {
|
512 |
|
513 | @Expose()
|
514 | id: number;
|
515 |
|
516 | @Expose()
|
517 | email: string;
|
518 |
|
519 | password: string;
|
520 | }
|
521 | ```
|
522 |
|
523 | Now `id` and `email` will be exposed, and password will be excluded during transformation.
|
524 | Alternatively, you can set exclusion strategy during transformation:
|
525 |
|
526 | ```javascript
|
527 | import {classToPlain} from "class-transformer";
|
528 | let photo = classToPlain(photo, { strategy: "excludeAll" });
|
529 | ```
|
530 |
|
531 | In this case you don't need to `@Exclude()` a whole class.
|
532 |
|
533 | ## Skipping private properties, or some prefixed properties
|
534 |
|
535 | If you name your private properties with a prefix, lets say with `_`,
|
536 | then you can exclude such properties from transformation too:
|
537 |
|
538 | ```javascript
|
539 | import {classToPlain} from "class-transformer";
|
540 | let photo = classToPlain(photo, { excludePrefixes: ["_"] });
|
541 | ```
|
542 |
|
543 | This will skip all properties that start with `_` prefix.
|
544 | You can pass any number of prefixes and all properties that begin with these prefixes will be ignored.
|
545 | For example:
|
546 |
|
547 | ```javascript
|
548 | import {Expose} from "class-transformer";
|
549 |
|
550 | export class User {
|
551 |
|
552 | id: number;
|
553 | private _firstName: string;
|
554 | private _lastName: string;
|
555 | _password: string;
|
556 |
|
557 | setName(firstName: string, lastName: string) {
|
558 | this._firstName = firstName;
|
559 | this._lastName = lastName;
|
560 | }
|
561 |
|
562 | @Expose()
|
563 | get name() {
|
564 | return this.firstName + " " + this.lastName;
|
565 | }
|
566 |
|
567 | }
|
568 |
|
569 | const user = new User();
|
570 | user.id = 1;
|
571 | user.setName("Johny", "Cage");
|
572 | user._password = 123;
|
573 |
|
574 | const plainUser = classToPlain(user, { excludePrefixes: ["_"] });
|
575 | // here plainUser will be equal to
|
576 | // { id: 1, name: "Johny Cage" }
|
577 | ```
|
578 |
|
579 | ## Using groups to control excluded properties
|
580 |
|
581 | You can use groups to control what data will be exposed and what will not be:
|
582 |
|
583 | ```javascript
|
584 | import {Exclude, Expose} from "class-transformer";
|
585 |
|
586 | @Exclude()
|
587 | export class User {
|
588 |
|
589 | id: number;
|
590 |
|
591 | name: string;
|
592 |
|
593 | @Expose({ groups: ["user", "admin"] }) // this means that this data will be exposed only to users and admins
|
594 | email: string;
|
595 |
|
596 | @Expose({ groups: ["user"] }) // this means that this data will be exposed only to users
|
597 | password: string;
|
598 | }
|
599 | ```
|
600 |
|
601 | ```javascript
|
602 | import {classToPlain} from "class-transformer";
|
603 | let user1 = classToPlain(user, { groups: ["user"] }); // will contain id, name, email and password
|
604 | let user2 = classToPlain(user, { groups: ["admin"] }); // will contain id, name and email
|
605 | ```
|
606 |
|
607 | ## Using versioning to control exposed and excluded properties
|
608 |
|
609 | If you are building an API that has different versions, class-transformer has extremely useful tools for that.
|
610 | You can control which properties of your model should be exposed or excluded in what version. Example:
|
611 |
|
612 | ```javascript
|
613 | import {Exclude, Expose} from "class-transformer";
|
614 |
|
615 | @Exclude()
|
616 | export class User {
|
617 |
|
618 | id: number;
|
619 |
|
620 | name: string;
|
621 |
|
622 | @Expose({ since: 0.7, until: 1 }) // this means that this property will be exposed for version starting from 0.7 until 1
|
623 | email: string;
|
624 |
|
625 | @Expose({ since: 2.1 }) // this means that this property will be exposed for version starting from 2.1
|
626 | password: string;
|
627 | }
|
628 | ```
|
629 |
|
630 | ```javascript
|
631 | import {classToPlain} from "class-transformer";
|
632 | let user1 = classToPlain(user, { version: 0.5 }); // will contain id and name
|
633 | let user2 = classToPlain(user, { version: 0.7 }); // will contain id, name and email
|
634 | let user3 = classToPlain(user, { version: 1 }); // will contain id and name
|
635 | let user4 = classToPlain(user, { version: 2 }); // will contain id and name
|
636 | let user5 = classToPlain(user, { version: 2.1 }); // will contain id, name nad password
|
637 | ```
|
638 |
|
639 | ## Сonverting date strings into Date objects
|
640 |
|
641 | Sometimes you have a Date in your plain javascript object received in a string format.
|
642 | And you want to create a real javascript Date object from it.
|
643 | You can do it simply by passing a Date object to the `@Type` decorator:
|
644 |
|
645 | ```javascript
|
646 | import {Type} from "class-transformer";
|
647 |
|
648 | export class User {
|
649 |
|
650 | id: number;
|
651 |
|
652 | email: string;
|
653 |
|
654 | password: string;
|
655 |
|
656 | @Type(() => Date)
|
657 | registrationDate: Date;
|
658 | }
|
659 | ```
|
660 |
|
661 | Note, that dates will be converted to strings when you'll try to convert class object to plain object.
|
662 |
|
663 | Same technique can be used with `Number`, `String`, `Boolean`
|
664 | primitive types when you want to convert your values into these types.
|
665 |
|
666 | ## Working with arrays
|
667 |
|
668 | When you are using arrays you must provide a type of the object that array contains.
|
669 | This type, you specify in a `@Type()` decorator:
|
670 |
|
671 | ```javascript
|
672 | import {Type} from "class-transformer";
|
673 |
|
674 | export class Photo {
|
675 |
|
676 | id: number;
|
677 |
|
678 | name: string;
|
679 |
|
680 | @Type(() => Album)
|
681 | albums: Album[];
|
682 | }
|
683 | ```
|
684 |
|
685 | You can also use custom array types:
|
686 |
|
687 | ```javascript
|
688 | import {Type} from "class-transformer";
|
689 |
|
690 | export class AlbumCollection extends Array<Album> {
|
691 | // custom array functions ...
|
692 | }
|
693 |
|
694 | export class Photo {
|
695 |
|
696 | id: number;
|
697 |
|
698 | name: string;
|
699 |
|
700 | @Type(() => Album)
|
701 | albums: AlbumCollection;
|
702 | }
|
703 | ```
|
704 |
|
705 | Library will handle proper transformation automatically.
|
706 |
|
707 | ES6 collections `Set` and `Map` also require the `@Type` decorator:
|
708 |
|
709 | ```javascript
|
710 | export class Skill {
|
711 | name: string;
|
712 | }
|
713 |
|
714 | export class Weapon {
|
715 | name: string;
|
716 | range: number;
|
717 | }
|
718 |
|
719 | export class Player {
|
720 | name: string;
|
721 |
|
722 | @Type(() => Skill)
|
723 | skills: Set<Skill>;
|
724 |
|
725 | @Type(() => Weapon)
|
726 | weapons: Map<string, Weapon>;
|
727 | }
|
728 | ```
|
729 |
|
730 | ## Additional data transformation
|
731 |
|
732 | ### Basic usage
|
733 |
|
734 | You can perform additional data transformation using `@Transform` decorator.
|
735 | For example, you want to make your `Date` object to be a `moment` object when you are
|
736 | transforming object from plain to class:
|
737 |
|
738 | ```javascript
|
739 | import {Transform} from "class-transformer";
|
740 | import * as moment from "moment";
|
741 | import {Moment} from "moment";
|
742 |
|
743 | export class Photo {
|
744 |
|
745 | id: number;
|
746 |
|
747 | @Type(() => Date)
|
748 | @Transform(value => moment(value), { toClassOnly: true })
|
749 | date: Moment;
|
750 | }
|
751 | ```
|
752 |
|
753 | Now when you call `plainToClass` and send a plain representation of the Photo object,
|
754 | it will convert a date value in your photo object to moment date.
|
755 | `@Transform` decorator also supports groups and versioning.
|
756 |
|
757 | ### Advanced usage
|
758 |
|
759 | The `@Transform` decorator is given more arguments to let you configure how you want the transformation to be done.
|
760 |
|
761 | ```
|
762 | @Transform((value, obj, type) => value)
|
763 | ```
|
764 |
|
765 | | Argument | Description
|
766 | |--------------------|---------------------------------------------------------------------------------|
|
767 | | `value` | The property value before the transformation.
|
768 | | `obj` | The transformation source object.
|
769 | | `type` | The transformation type.
|
770 |
|
771 | ## Other decorators
|
772 | | Signature | Example | Description
|
773 | |--------------------|------------------------------------------|---------------------------------------------|
|
774 | | `@TransformClassToPlain` | `@TransformClassToPlain({ groups: ["user"] })` | Transform the method return with classToPlain and expose the properties on the class.
|
775 | | `@TransformClassToClass` | `@TransformClassToClass({ groups: ["user"] })` | Transform the method return with classToClass and expose the properties on the class.
|
776 | | `@TransformPlainToClass` | `@TransformPlainToClass(User, { groups: ["user"] })` | Transform the method return with plainToClass and expose the properties on the class.
|
777 |
|
778 | The above decorators accept one optional argument:
|
779 | ClassTransformOptions - The transform options like groups, version, name
|
780 |
|
781 | An example:
|
782 |
|
783 | ```javascript
|
784 | @Exclude()
|
785 | class User {
|
786 |
|
787 | id: number;
|
788 |
|
789 | @Expose()
|
790 | firstName: string;
|
791 |
|
792 | @Expose()
|
793 | lastName: string;
|
794 |
|
795 | @Expose({ groups: ['user.email'] })
|
796 | email: string;
|
797 |
|
798 | password: string;
|
799 | }
|
800 |
|
801 | class UserController {
|
802 |
|
803 | @TransformClassToPlain({ groups: ['user.email'] })
|
804 | getUser() {
|
805 | const user = new User();
|
806 | user.firstName = "Snir";
|
807 | user.lastName = "Segal";
|
808 | user.password = "imnosuperman";
|
809 |
|
810 | return user;
|
811 | }
|
812 | }
|
813 |
|
814 | const controller = new UserController();
|
815 | const user = controller.getUser();
|
816 | ```
|
817 |
|
818 | the `user` variable will contain only firstName,lastName, email properties becuase they are
|
819 | the exposed variables. email property is also exposed becuase we metioned the group "user.email".
|
820 |
|
821 | ## Working with generics
|
822 |
|
823 | Generics are not supported because TypeScript does not have good reflection abilities yet.
|
824 | Once TypeScript team provide us better runtime type reflection tools, generics will be implemented.
|
825 | There are some tweaks however you can use, that maybe can solve your problem.
|
826 | [Checkout this example.](https://github.com/pleerock/class-transformer/tree/master/sample/sample4-generics)
|
827 |
|
828 | ## How does it handle circular references?
|
829 |
|
830 | Circular references are ignored.
|
831 | For example, if you are transforming class `User` that contains property `photos` with type of `Photo`,
|
832 | and `Photo` contains link `user` to its parent `User`, then `user` will be ignored during transformation.
|
833 | Circular references are not ignored only during `classToClass` operation.
|
834 |
|
835 | ## Example with Angular2
|
836 |
|
837 | Lets say you want to download users and want them automatically to be mapped to the instances of `User` class.
|
838 |
|
839 | ```javascript
|
840 | import {plainToClass} from "class-transformer";
|
841 |
|
842 | this.http
|
843 | .get("users.json")
|
844 | .map(res => res.json())
|
845 | .map(res => plainToClass(User, res as Object[]))
|
846 | .subscribe(users => {
|
847 | // now "users" is type of User[] and each user has getName() and isAdult() methods available
|
848 | console.log(users);
|
849 | });
|
850 | ```
|
851 |
|
852 | You can also inject a class `ClassTransformer` as a service in `providers`, and use its methods.
|
853 |
|
854 | Example how to use with angular 2 in [plunker](http://plnkr.co/edit/Mja1ZYAjVySWASMHVB9R).
|
855 | Source code is [here](https://github.com/pleerock/class-transformer-demo).
|
856 |
|
857 | ## Samples
|
858 |
|
859 | Take a look on samples in [./sample](https://github.com/pleerock/class-transformer/tree/master/sample) for more examples of
|
860 | usages.
|
861 |
|
862 | ## Release notes
|
863 |
|
864 | See information about breaking changes and release notes [here](https://github.com/pleerock/class-transformer/tree/master/doc/release-notes.md).
|