DTO Design in TypeScript / Angular2

I am currently developing an Angular 2 application. During development, I started using TypeScript classes to create objects from JSON that I receive via HTTP or when creating a new object in the form.

A class may look, for example, in this way.

export class Product { public id: number; public name: string; public description: string; public price: number; private _imageId: number; private _imageUrl: string; constructor(obj: Object = {}) { Object.assign(this, obj); } get imageId(): number { return this._imageId; } set imageId(id: number) { this._imageId = id; this._imageUrl = `//www.example.org/images/${id}`; } get imageUrl(): string { return this._imageUrl; } public getDTO() { return { name: this.name, description: this.description, imageId: this.imageId, price: this.price } } } 

So far, this solution, shown above, works great. But now suppose the object has a lot more properties, and I want a clean DTO (for example, without private properties) to send this POST object to my server. What might the more general getDTO() function look like? I would like to avoid a long list of property assignments. I was thinking about using decorators for properties. But I do not know how to use them to filter properties for DTO.

+5
source share
1 answer

You can use the property decorator to do this:

 const DOT_INCLUDES = {}; function DtoInclude(proto, name) { const key = proto.constructor.name; if (DOT_INCLUDES[key]) { DOT_INCLUDES[key].push(name); } else { DOT_INCLUDES[key] = [name]; } } class A { @DtoInclude public x: number; public y: number; @DtoInclude private str: string; constructor(x: number, y: number, str: string) { this.x = x; this.y = y; this.str = str; } toDTO(): any { const includes: string[] = DOT_INCLUDES[(this.constructor as any).name]; const dto = {}; for (let key in this) { if (includes.indexOf(key) >= 0) { dto[key] = this[key]; } } return dto; } } let a = new A(1, 2, "string"); console.log(a.toDTO()); // Object {x: 1, str: "string"} 

( code on the playground )

You can use the reflection metadata that is used in their examples, if you want, I implemented it with the DOT_INCLUDES registry DOT_INCLUDES that it will work well on the playground, without requiring additional dependencies.


Edit

As @Bergi noted, you can iterate over includes instead of this :

 toDTO(): any { const includes: string[] = DOT_INCLUDES[(this.constructor as any).name]; const dto = {}; for (let ket of includes) { dto[key] = this[key]; } return dto; } 

Which is really more efficient and makes more sense.

+3
source

All Articles