diff --git a/README.md b/README.md index 59d9eda..c23109f 100644 --- a/README.md +++ b/README.md @@ -1,847 +1,915 @@ -# class-transformer deno - -`deno port of class-transformer` - -[typestack/class-transformer](https://github.com/typestack/class-transformer)@v0.2.3 - -Its ES6 and Typescript era. Nowadays you are working with classes and constructor objects more then ever. -Class-transformer allows you to transform plain object to some instance of class and versa. -Also it allows to serialize / deserialize object based on criteria. -This tool is super useful on both frontend and backend. - -## What is class-transformer - -In JavaScript there are two types of objects: - -- plain (literal) objects -- class (constructor) objects - -Plain objects are objects that are instances of `Object` class. -Sometimes they are called **literal** objects, when created via `{}` notation. -Class objects are instances of classes with own defined constructor, properties and methods. -Usually you define them via `class` notation. - -So, what is the problem? - -Sometimes you want to transform plain javascript object to the ES6 **classes** you have. -For example, if you are loading a json from your backend, some api or from a json file, -and after you `JSON.parse` it you have a plain javascript object, not instance of class you have. - -For example you have a list of users in your `users.json` that you are loading: - -```json -[ - { - "id": 1, - "firstName": "Johny", - "lastName": "Cage", - "age": 27 - }, - { - "id": 2, - "firstName": "Ismoil", - "lastName": "Somoni", - "age": 50 - }, - { - "id": 3, - "firstName": "Luke", - "lastName": "Dacascos", - "age": 12 - } -] -``` - -And you have a `User` class: - -```typescript -export class User { - id: number; - firstName: string; - lastName: string; - age: number; - - getName() { - return this.firstName + " " + this.lastName; - } - - isAdult() { - return this.age > 36 && this.age < 60; - } -} -``` - -You are assuming that you are downloading users of type `User` from `users.json` file and may want to write -following code: - -```typescript -fetch("users.json").then((users: User[]) => { - /** - * you can use users here, and type hinting also will be available to you, - * but users are not actually instances of User class - * this means that you can't use methods of User class - */ -}); -``` - -In this code you can use `users[0].id`, you can also use `users[0].firstName` and `users[0].lastName`. -However you cannot use `users[0].getName()` or `users[0].isAdult()` because "users" actually is -array of plain javascript objects, not instances of User object. -You actually lied to compiler when you said that its `users: User[]`. - -So what to do? How to make a `users` array of instances of `User` objects instead of plain javascript objects? -Solution is to create new instances of User object and manually copy all properties to new objects. -But things may go wrong very fast once you have a more complex object hierarchy. - -Alternatives? Yes, you can use class-transformer. Purpose of this library is to help you to map you plain javascript -objects to the instances of classes you have. - -This library also great for models exposed in your APIs, -because it provides a great tooling to control what your models are exposing in your API. -Here is example how it will look like: - -```typescript -fetch("users.json").then((users: Object[]) => { - const realUsers = plainToClass(User, users); - // now each user in realUsers is instance of User class -}); -``` - -Now you can use `users[0].getName()` and `users[0].isAdult()` methods. - -## Installation - -### Deno - -1. Import module: - - ```ts - import { plainToClass } from "https://raw.githubusercontent.com/tnifey/class-transformer/master/mod.ts"; - ``` - - or use importmap - - ```json - { - "imports": { - "class-transformer": "https://raw.githubusercontent.com/tnifey/class-transformer/master/" - } - } - ``` - - use like - - ```ts - import { Expose } from "class-transformer/mod.ts"; - ``` - -2. `reflect-metadata` shim is required, install it too: - - ```ts - import "https://raw.githubusercontent.com/rbuckton/reflect-metadata/master/Reflect.ts"; - ``` - - and make sure to import it in a global place. - -## Methods - -#### plainToClass - -This method transforms a plain javascript object to instance of specific class. - -```typescript -import { plainToClass } from "class-transformer/mod.ts"; - -let users = plainToClass(User, userJson); // to convert user plain object a single user. also supports arrays -``` - -#### plainToClassFromExist - -This method transforms a plain object into a instance using a already filled Object which is a instance from the target class. - -```typescript -const defaultUser = new User(); -defaultUser.role = "user"; - -let mixedUser = plainToClassFromExist(defaultUser, user); // mixed user should have the value role = user when no value is set otherwise. -``` - -#### classToPlain - -This method transforms your class object back to plain javascript object, that can be `JSON.stringify` later. - -```typescript -import { classToPlain } from "class-transformer/mod.ts"; -let photo = classToPlain(photo); -``` - -#### classToClass - -This method transforms your class object into new instance of the class object. -This maybe treated as deep clone of your objects. - -```typescript -import { classToClass } from "class-transformer/mod.ts"; -let photo = classToClass(photo); -``` - -You can also use a `ignoreDecorators` option in transformation options to ignore all decorators you classes is using. - -#### serialize - -You can serialize your model right to the json using `serialize` method: - -```typescript -import { serialize } from "class-transformer/mod.ts"; -let photo = serialize(photo); -``` - -`serialize` works with both arrays and non-arrays. - -#### deserialize and deserializeArray - -You can deserialize your model to from a json using `deserialize` method: - -```typescript -import { deserialize } from "class-transformer/mod.ts"; -let photo = deserialize(Photo, photo); -``` - -To make deserialization to work with arrays use `deserializeArray` method: - -```typescript -import { deserializeArray } from "class-transformer/mod.ts"; -let photos = deserializeArray(Photo, photos); -``` - -## Enforcing type-safe instance - -The default behaviour of the `plainToClass` method is to set _all_ properties from the plain object, -even those which are not specified in the class. - -```typescript -import { plainToClass } from "class-transformer/mod.ts"; - -class User { - id: number; - firstName: string; - lastName: string; -} - -const fromPlainUser = { - unkownProp: "hello there", - firstName: "Umed", - lastName: "Khudoiberdiev", -}; - -console.log(plainToClass(User, fromPlainUser)); - -// User { -// unkownProp: 'hello there', -// firstName: 'Umed', -// lastName: 'Khudoiberdiev', -// } -``` - -If this behaviour does not suit your needs, you can use the `excludeExtraneousValues` option -in the `plainToClass` method while _exposing all your class properties_ as a requirement. - -```typescript -import { Expose, plainToClass } from "class-transformer/mod.ts"; - -class User { - @Expose() id: number; - @Expose() firstName: string; - @Expose() lastName: string; -} - -const fromPlainUser = { - unkownProp: "hello there", - firstName: "Umed", - lastName: "Khudoiberdiev", -}; - -console.log( - plainToClass(User, fromPlainUser, { excludeExtraneousValues: true }) -); - -// User { -// id: undefined, -// firstName: 'Umed', -// lastName: 'Khudoiberdiev' -// } -``` - -## Working with nested objects - -When you are trying to transform objects that have nested objects, -its required to known what type of object you are trying to transform. -Since Typescript does not have good reflection abilities yet, -we should implicitly specify what type of object each property contain. -This is done using `@Type` decorator. - -Lets say we have an album with photos. -And we are trying to convert album plain object to class object: - -```typescript -import { Type, plainToClass } from "class-transformer/mod.ts"; - -export class Album { - id: number; - - name: string; - - @Type(() => Photo) - photos: Photo[]; -} - -export class Photo { - id: number; - filename: string; -} - -let album = plainToClass(Album, albumJson); -// now album is Album object with Photo objects inside -``` - -### Providing more than one type option - -In case the nested object can be of different types, you can provide an additional options object, -that specifies a discriminator. The discriminator option must define a `property` that holds the sub -type name for the object and the possible `subTypes`, the nested object can converted to. A sub type -has a `value`, that holds the constructor of the Type and the `name`, that can match with the `property` -of the discriminator. - -Lets say we have an album that has a top photo. But this photo can be of certain different types. -And we are trying to convert album plain object to class object. The plain object input has to define -the additional property `__type`. This property is removed during transformation by default: - -**JSON input**: - -```json -{ - "id": 1, - "name": "foo", - "topPhoto": { - "id": 9, - "filename": "cool_wale.jpg", - "depth": 1245, - "__type": "underwater" - } -} -``` - -```typescript -import { Type, plainToClass } from "class-transformer/mod.ts"; - -export abstract class Photo { - id: number; - filename: string; -} - -export class Landscape extends Photo { - panorama: boolean; -} - -export class Portrait extends Photo { - person: Person; -} - -export class UnderWater extends Photo { - depth: number; -} - -export class Album { - id: number; - name: string; - - @Type(() => Photo, { - discriminator: { - property: "__type", - subTypes: [ - { value: Landscape, name: "landscape" }, - { value: Portrait, name: "portrait" }, - { value: UnderWater, name: "underwater" }, - ], - }, - }) - topPhoto: Landscape | Portrait | UnderWater; -} - -let album = plainToClass(Album, albumJson); -// now album is Album object with a UnderWater object without `__type` property. -``` - -Hint: The same applies for arrays with different sub types. Moreover you can specify `keepDiscriminatorProperty: true` -in the options to keep the discriminator property also inside your resulting class. - -## Exposing getters and method return values - -You can expose what your getter or method return by setting a `@Expose()` decorator to those getters or methods: - -```typescript -import { Expose } from "class-transformer"; - -export class User { - id: number; - firstName: string; - lastName: string; - password: string; - - @Expose() - get name() { - return this.firstName + " " + this.lastName; - } - - @Expose() - getFullName() { - return this.firstName + " " + this.lastName; - } -} -``` - -## Exposing properties with different names - -If you want to expose some of properties with a different name, -you can do it by specifying a `name` option to `@Expose` decorator: - -```typescript -import { Expose } from "class-transformer/mod.ts"; - -export class User { - @Expose({ name: "uid" }) - id: number; - - firstName: string; - - lastName: string; - - @Expose({ name: "secretKey" }) - password: string; - - @Expose({ name: "fullName" }) - getFullName() { - return this.firstName + " " + this.lastName; - } -} -``` - -## Skipping specific properties - -Sometimes you want to skip some properties during transformation. -This can be done using `@Exclude` decorator: - -```typescript -import { Exclude } from "class-transformer/mod.ts"; - -export class User { - id: number; - - email: string; - - @Exclude() - password: string; -} -``` - -Now when you transform a User, `password` property will be skipped and not be included in the transformed result. - -## Skipping depend of operation - -You can control on what operation you will exclude a property. Use `toClassOnly` or `toPlainOnly` options: - -```typescript -import { Exclude } from "class-transformer/mod.ts"; - -export class User { - id: number; - - email: string; - - @Exclude({ toPlainOnly: true }) - password: string; -} -``` - -Now `password` property will be excluded only during `classToPlain` operation. Oppositely, use `toClassOnly` option. - -## Skipping all properties of the class - -You can skip all properties of the class, and expose only those are needed explicitly: - -```typescript -import { Exclude, Expose } from "class-transformer/mod.ts"; - -@Exclude() -export class User { - @Expose() - id: number; - - @Expose() - email: string; - - password: string; -} -``` - -Now `id` and `email` will be exposed, and password will be excluded during transformation. -Alternatively, you can set exclusion strategy during transformation: - -```typescript -import { classToPlain } from "class-transformer/mod.ts"; -let photo = classToPlain(photo, { strategy: "excludeAll" }); -``` - -In this case you don't need to `@Exclude()` a whole class. - -## Skipping private properties, or some prefixed properties - -If you name your private properties with a prefix, lets say with `_`, -then you can exclude such properties from transformation too: - -```typescript -import { classToPlain } from "class-transformer/mod.ts"; -let photo = classToPlain(photo, { excludePrefixes: ["_"] }); -``` - -This will skip all properties that start with `_` prefix. -You can pass any number of prefixes and all properties that begin with these prefixes will be ignored. -For example: - -```typescript -import { Expose } from "class-transformer/mod.ts"; - -export class User { - id: number; - private _firstName: string; - private _lastName: string; - _password: string; - - setName(firstName: string, lastName: string) { - this._firstName = firstName; - this._lastName = lastName; - } - - @Expose() - get name() { - return this.firstName + " " + this.lastName; - } -} - -const user = new User(); -user.id = 1; -user.setName("Johny", "Cage"); -user._password = 123; - -const plainUser = classToPlain(user, { excludePrefixes: ["_"] }); -// here plainUser will be equal to -// { id: 1, name: "Johny Cage" } -``` - -## Using groups to control excluded properties - -You can use groups to control what data will be exposed and what will not be: - -```typescript -import { Exclude, Expose } from "class-transformer/mod.ts"; - -@Exclude() -export class User { - id: number; - - name: string; - - @Expose({ groups: ["user", "admin"] }) // this means that this data will be exposed only to users and admins - email: string; - - @Expose({ groups: ["user"] }) // this means that this data will be exposed only to users - password: string; -} -``` - -```typescript -import { classToPlain } from "class-transformer/mod.ts"; -let user1 = classToPlain(user, { groups: ["user"] }); // will contain id, name, email and password -let user2 = classToPlain(user, { groups: ["admin"] }); // will contain id, name and email -``` - -## Using versioning to control exposed and excluded properties - -If you are building an API that has different versions, class-transformer has extremely useful tools for that. -You can control which properties of your model should be exposed or excluded in what version. Example: - -```typescript -import { Exclude, Expose } from "class-transformer/mod.ts"; - -@Exclude() -export class User { - id: number; - - name: string; - - @Expose({ since: 0.7, until: 1 }) // this means that this property will be exposed for version starting from 0.7 until 1 - email: string; - - @Expose({ since: 2.1 }) // this means that this property will be exposed for version starting from 2.1 - password: string; -} -``` - -```typescript -import { classToPlain } from "class-transformer/mod.ts"; -let user1 = classToPlain(user, { version: 0.5 }); // will contain id and name -let user2 = classToPlain(user, { version: 0.7 }); // will contain id, name and email -let user3 = classToPlain(user, { version: 1 }); // will contain id and name -let user4 = classToPlain(user, { version: 2 }); // will contain id and name -let user5 = classToPlain(user, { version: 2.1 }); // will contain id, name nad password -``` - -## Сonverting date strings into Date objects - -Sometimes you have a Date in your plain javascript object received in a string format. -And you want to create a real javascript Date object from it. -You can do it simply by passing a Date object to the `@Type` decorator: - -```typescript -import { Type } from "class-transformer/mod.ts"; - -export class User { - id: number; - - email: string; - - password: string; - - @Type(() => Date) - registrationDate: Date; -} -``` - -Note, that dates will be converted to strings when you'll try to convert class object to plain object. - -Same technique can be used with `Number`, `String`, `Boolean` -primitive types when you want to convert your values into these types. - -## Working with arrays - -When you are using arrays you must provide a type of the object that array contains. -This type, you specify in a `@Type()` decorator: - -```typescript -import { Type } from "class-transformer/mod.ts"; - -export class Photo { - id: number; - - name: string; - - @Type(() => Album) - albums: Album[]; -} -``` - -You can also use custom array types: - -```typescript -import { Type } from "class-transformer/mod.ts"; - -export class AlbumCollection extends Array { - // custom array functions ... -} - -export class Photo { - id: number; - - name: string; - - @Type(() => Album) - albums: AlbumCollection; -} -``` - -Library will handle proper transformation automatically. - -ES6 collections `Set` and `Map` also require the `@Type` decorator: - -```typescript -export class Skill { - name: string; -} - -export class Weapon { - name: string; - range: number; -} - -export class Player { - name: string; - - @Type(() => Skill) - skills: Set; - - @Type(() => Weapon) - weapons: Map; -} -``` - -## Additional data transformation - -### Basic usage - -You can perform additional data transformation using `@Transform` decorator. -For example, you want to make your `Date` object to be a `moment` object when you are -transforming object from plain to class: - -```typescript -import { Transform } from "class-transformer/mod.ts"; -import * as moment from "https://dev.jspm.io/npm:moment"; -import { Moment } from "https://dev.jspm.io/npm:moment"; - -export class Photo { - id: number; - - @Type(() => Date) - @Transform((value) => moment(value), { toClassOnly: true }) - date: Moment; -} -``` - -Now when you call `plainToClass` and send a plain representation of the Photo object, -it will convert a date value in your photo object to moment date. -`@Transform` decorator also supports groups and versioning. - -### Advanced usage - -The `@Transform` decorator is given more arguments to let you configure how you want the transformation to be done. - -``` -@Transform((value, obj, type) => value) -``` - -| Argument | Description | -| -------- | --------------------------------------------- | -| `value` | The property value before the transformation. | -| `obj` | The transformation source object. | -| `type` | The transformation type. | - -## Other decorators - -| Signature | Example | Description | -| ------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------- | -| `@TransformClassToPlain` | `@TransformClassToPlain({ groups: ["user"] })` | Transform the method return with classToPlain and expose the properties on the class. | -| `@TransformClassToClass` | `@TransformClassToClass({ groups: ["user"] })` | Transform the method return with classToClass and expose the properties on the class. | -| `@TransformPlainToClass` | `@TransformPlainToClass(User, { groups: ["user"] })` | Transform the method return with plainToClass and expose the properties on the class. | - -The above decorators accept one optional argument: -ClassTransformOptions - The transform options like groups, version, name - -An example: - -```typescript -@Exclude() -class User { - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - @Expose({ groups: ["user.email"] }) - email: string; - - password: string; -} - -class UserController { - @TransformClassToPlain({ groups: ["user.email"] }) - getUser() { - const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; - - return user; - } -} - -const controller = new UserController(); -const user = controller.getUser(); -``` - -the `user` variable will contain only firstName,lastName, email properties becuase they are -the exposed variables. email property is also exposed becuase we metioned the group "user.email". - -## Working with generics - -Generics are not supported because TypeScript does not have good reflection abilities yet. -Once TypeScript team provide us better runtime type reflection tools, generics will be implemented. -There are some tweaks however you can use, that maybe can solve your problem. -[Checkout this example.](https://github.com/pleerock/class-transformer/tree/master/sample/sample4-generics) - -## Implicit type conversion - -> **NOTE** If you use class-validator together with class-transformer you propably DON'T want to enable this function. - -Enables automatic conversion between built-in types based on type information provided by Typescript. Disabled by default. - -```ts -import { IsString } from "class-validator/mod.ts"; - -class MyPayload { - @IsString() - prop: string; -} - -const result1 = plainToClass( - MyPayload, - { prop: 1234 }, - { enableImplicitConversion: true } -); -const result2 = plainToClass( - MyPayload, - { prop: 1234 }, - { enableImplicitConversion: false } -); - -/** - * result1 will be `{ prop: "1234" }` - notice how the prop value has been converted to string. - * result2 will be `{ prop: 1234 }` - default behaviour - */ -``` - -## How does it handle circular references? - -Circular references are ignored. -For example, if you are transforming class `User` that contains property `photos` with type of `Photo`, -and `Photo` contains link `user` to its parent `User`, then `user` will be ignored during transformation. -Circular references are not ignored only during `classToClass` operation. - -## Example with Angular2 - -Lets say you want to download users and want them automatically to be mapped to the instances of `User` class. - -```typescript -import { plainToClass } from "class-transformer/mod.ts"; - -this.http - .get("users.json") - .map((res) => res.json()) - .map((res) => plainToClass(User, res as Object[])) - .subscribe((users) => { - // now "users" is type of User[] and each user has getName() and isAdult() methods available - console.log(users); - }); -``` - -You can also inject a class `ClassTransformer` as a service in `providers`, and use its methods. - -## Samples - -Take a look on samples in [./sample](https://github.com/pleerock/class-transformer/tree/master/sample) for more examples of -usages. - -## Release notes - -See information about breaking changes and release notes [here](https://github.com/pleerock/class-transformer/tree/master/doc/release-notes.md). +# use transpiled `class-transformer` instead + +```typescript +// import "reflect-metadata" into your main file +import "https://esm.sh/reflect-metadata"; // you need to --allow-env + +import { Expose, plainToClass } from "https://esm.sh/class-transformer"; + +class SomeDto { + @Expose() + name: string; + + @Expose() + value: string; +} + +const asClass = plainToClass(SomeDto, { + name: "something", + value: 2345, +}); +``` + +## class-transformer deno (deprecated) + +`deno port of class-transformer` + +[typestack/class-transformer](https://github.com/typestack/class-transformer)@v0.2.3 + +``` +Loosely tested on +deno 1.8.1 (release, x86_64-unknown-linux-gnu) +v8 9.0.257.3 +typescript 4.2.2 +``` + +with tsconfig.json + +```json +{ + "compilerOptions": { + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false + } +} +``` + +Its ES6 and Typescript era. Nowadays you are working with classes and +constructor objects more then ever. Class-transformer allows you to transform +plain object to some instance of class and versa. Also it allows to serialize / +deserialize object based on criteria. This tool is super useful on both frontend +and backend. + +## What is class-transformer + +In JavaScript there are two types of objects: + +- plain (literal) objects +- class (constructor) objects + +Plain objects are objects that are instances of `Object` class. Sometimes they +are called **literal** objects, when created via `{}` notation. Class objects +are instances of classes with own defined constructor, properties and methods. +Usually you define them via `class` notation. + +So, what is the problem? + +Sometimes you want to transform plain javascript object to the ES6 **classes** +you have. For example, if you are loading a json from your backend, some api or +from a json file, and after you `JSON.parse` it you have a plain javascript +object, not instance of class you have. + +For example you have a list of users in your `users.json` that you are loading: + +```json +[ + { + "id": 1, + "firstName": "Johny", + "lastName": "Cage", + "age": 27 + }, + { + "id": 2, + "firstName": "Ismoil", + "lastName": "Somoni", + "age": 50 + }, + { + "id": 3, + "firstName": "Luke", + "lastName": "Dacascos", + "age": 12 + } +] +``` + +And you have a `User` class: + +```typescript +export class User { + id: number; + firstName: string; + lastName: string; + age: number; + + getName() { + return this.firstName + " " + this.lastName; + } + + isAdult() { + return this.age > 36 && this.age < 60; + } +} +``` + +You are assuming that you are downloading users of type `User` from `users.json` +file and may want to write following code: + +```typescript +fetch("users.json").then((users: User[]) => { + /** + * you can use users here, and type hinting also will be available to you, + * but users are not actually instances of User class + * this means that you can't use methods of User class + */ +}); +``` + +In this code you can use `users[0].id`, you can also use `users[0].firstName` +and `users[0].lastName`. However you cannot use `users[0].getName()` or +`users[0].isAdult()` because "users" actually is array of plain javascript +objects, not instances of User object. You actually lied to compiler when you +said that its `users: User[]`. + +So what to do? How to make a `users` array of instances of `User` objects +instead of plain javascript objects? Solution is to create new instances of User +object and manually copy all properties to new objects. But things may go wrong +very fast once you have a more complex object hierarchy. + +Alternatives? Yes, you can use class-transformer. Purpose of this library is to +help you to map you plain javascript objects to the instances of classes you +have. + +This library also great for models exposed in your APIs, because it provides a +great tooling to control what your models are exposing in your API. Here is +example how it will look like: + +```typescript +fetch("users.json").then((users: Object[]) => { + const realUsers = plainToClass(User, users); + // now each user in realUsers is instance of User class +}); +``` + +Now you can use `users[0].getName()` and `users[0].isAdult()` methods. + +## Installation + +### Deno + +1. Import module: + + ```ts + import { plainToClass } from "https://raw.githubusercontent.com/tnifey/class-transformer/master/mod.ts"; + ``` + + or use importmap + + ```json + { + "imports": { + "class-transformer": "https://raw.githubusercontent.com/tnifey/class-transformer/master/" + } + } + ``` + + use like + + ```ts + import { Expose } from "class-transformer/mod.ts"; + ``` + +2. `reflect-metadata` shim is required, install it too: + + ```ts + // you need to --allow-env + import "https://esm.sh/reflect-metadata"; + ``` + + and make sure to import it in a global place. + +## Methods + +#### plainToClass + +This method transforms a plain javascript object to instance of specific class. + +```typescript +import { plainToClass } from "class-transformer/mod.ts"; + +let users = plainToClass(User, userJson); // to convert user plain object a single user. also supports arrays +``` + +#### plainToClassFromExist + +This method transforms a plain object into a instance using a already filled +Object which is a instance from the target class. + +```typescript +const defaultUser = new User(); +defaultUser.role = "user"; + +let mixedUser = plainToClassFromExist(defaultUser, user); // mixed user should have the value role = user when no value is set otherwise. +``` + +#### classToPlain + +This method transforms your class object back to plain javascript object, that +can be `JSON.stringify` later. + +```typescript +import { classToPlain } from "class-transformer/mod.ts"; +let photo = classToPlain(photo); +``` + +#### classToClass + +This method transforms your class object into new instance of the class object. +This maybe treated as deep clone of your objects. + +```typescript +import { classToClass } from "class-transformer/mod.ts"; +let photo = classToClass(photo); +``` + +You can also use a `ignoreDecorators` option in transformation options to ignore +all decorators you classes is using. + +#### serialize + +You can serialize your model right to the json using `serialize` method: + +```typescript +import { serialize } from "class-transformer/mod.ts"; +let photo = serialize(photo); +``` + +`serialize` works with both arrays and non-arrays. + +#### deserialize and deserializeArray + +You can deserialize your model to from a json using `deserialize` method: + +```typescript +import { deserialize } from "class-transformer/mod.ts"; +let photo = deserialize(Photo, photo); +``` + +To make deserialization to work with arrays use `deserializeArray` method: + +```typescript +import { deserializeArray } from "class-transformer/mod.ts"; +let photos = deserializeArray(Photo, photos); +``` + +## Enforcing type-safe instance + +The default behaviour of the `plainToClass` method is to set _all_ properties +from the plain object, even those which are not specified in the class. + +```typescript +import { plainToClass } from "class-transformer/mod.ts"; + +class User { + id: number; + firstName: string; + lastName: string; +} + +const fromPlainUser = { + unkownProp: "hello there", + firstName: "Umed", + lastName: "Khudoiberdiev", +}; + +console.log(plainToClass(User, fromPlainUser)); + +// User { +// unkownProp: 'hello there', +// firstName: 'Umed', +// lastName: 'Khudoiberdiev', +// } +``` + +If this behaviour does not suit your needs, you can use the +`excludeExtraneousValues` option in the `plainToClass` method while _exposing +all your class properties_ as a requirement. + +```typescript +import { Expose, plainToClass } from "class-transformer/mod.ts"; + +class User { + @Expose() + id: number; + @Expose() + firstName: string; + @Expose() + lastName: string; +} + +const fromPlainUser = { + unkownProp: "hello there", + firstName: "Umed", + lastName: "Khudoiberdiev", +}; + +console.log( + plainToClass(User, fromPlainUser, { excludeExtraneousValues: true }) +); + +// User { +// id: undefined, +// firstName: 'Umed', +// lastName: 'Khudoiberdiev' +// } +``` + +## Working with nested objects + +When you are trying to transform objects that have nested objects, its required +to known what type of object you are trying to transform. Since Typescript does +not have good reflection abilities yet, we should implicitly specify what type +of object each property contain. This is done using `@Type` decorator. + +Lets say we have an album with photos. And we are trying to convert album plain +object to class object: + +```typescript +import { plainToClass, Type } from "class-transformer/mod.ts"; + +export class Album { + id: number; + + name: string; + + @Type(() => Photo) + photos: Photo[]; +} + +export class Photo { + id: number; + filename: string; +} + +let album = plainToClass(Album, albumJson); +// now album is Album object with Photo objects inside +``` + +### Providing more than one type option + +In case the nested object can be of different types, you can provide an +additional options object, that specifies a discriminator. The discriminator +option must define a `property` that holds the sub type name for the object and +the possible `subTypes`, the nested object can converted to. A sub type has a +`value`, that holds the constructor of the Type and the `name`, that can match +with the `property` of the discriminator. + +Lets say we have an album that has a top photo. But this photo can be of certain +different types. And we are trying to convert album plain object to class +object. The plain object input has to define the additional property `__type`. +This property is removed during transformation by default: + +**JSON input**: + +```json +{ + "id": 1, + "name": "foo", + "topPhoto": { + "id": 9, + "filename": "cool_wale.jpg", + "depth": 1245, + "__type": "underwater" + } +} +``` + +```typescript +import { plainToClass, Type } from "class-transformer/mod.ts"; + +export abstract class Photo { + id: number; + filename: string; +} + +export class Landscape extends Photo { + panorama: boolean; +} + +export class Portrait extends Photo { + person: Person; +} + +export class UnderWater extends Photo { + depth: number; +} + +export class Album { + id: number; + name: string; + + @Type(() => Photo, { + discriminator: { + property: "__type", + subTypes: [ + { value: Landscape, name: "landscape" }, + { value: Portrait, name: "portrait" }, + { value: UnderWater, name: "underwater" }, + ], + }, + }) + topPhoto: Landscape | Portrait | UnderWater; +} + +let album = plainToClass(Album, albumJson); +// now album is Album object with a UnderWater object without `__type` property. +``` + +Hint: The same applies for arrays with different sub types. Moreover you can +specify `keepDiscriminatorProperty: true` in the options to keep the +discriminator property also inside your resulting class. + +## Exposing getters and method return values + +You can expose what your getter or method return by setting a `@Expose()` +decorator to those getters or methods: + +```typescript +import { Expose } from "class-transformer"; + +export class User { + id: number; + firstName: string; + lastName: string; + password: string; + + @Expose() + get name() { + return this.firstName + " " + this.lastName; + } + + @Expose() + getFullName() { + return this.firstName + " " + this.lastName; + } +} +``` + +## Exposing properties with different names + +If you want to expose some of properties with a different name, you can do it by +specifying a `name` option to `@Expose` decorator: + +```typescript +import { Expose } from "class-transformer/mod.ts"; + +export class User { + @Expose({ name: "uid" }) + id: number; + + firstName: string; + + lastName: string; + + @Expose({ name: "secretKey" }) + password: string; + + @Expose({ name: "fullName" }) + getFullName() { + return this.firstName + " " + this.lastName; + } +} +``` + +## Skipping specific properties + +Sometimes you want to skip some properties during transformation. This can be +done using `@Exclude` decorator: + +```typescript +import { Exclude } from "class-transformer/mod.ts"; + +export class User { + id: number; + + email: string; + + @Exclude() + password: string; +} +``` + +Now when you transform a User, `password` property will be skipped and not be +included in the transformed result. + +## Skipping depend of operation + +You can control on what operation you will exclude a property. Use `toClassOnly` +or `toPlainOnly` options: + +```typescript +import { Exclude } from "class-transformer/mod.ts"; + +export class User { + id: number; + + email: string; + + @Exclude({ toPlainOnly: true }) + password: string; +} +``` + +Now `password` property will be excluded only during `classToPlain` operation. +Oppositely, use `toClassOnly` option. + +## Skipping all properties of the class + +You can skip all properties of the class, and expose only those are needed +explicitly: + +```typescript +import { Exclude, Expose } from "class-transformer/mod.ts"; + +@Exclude() +export class User { + @Expose() + id: number; + + @Expose() + email: string; + + password: string; +} +``` + +Now `id` and `email` will be exposed, and password will be excluded during +transformation. Alternatively, you can set exclusion strategy during +transformation: + +```typescript +import { classToPlain } from "class-transformer/mod.ts"; +let photo = classToPlain(photo, { strategy: "excludeAll" }); +``` + +In this case you don't need to `@Exclude()` a whole class. + +## Skipping private properties, or some prefixed properties + +If you name your private properties with a prefix, lets say with `_`, then you +can exclude such properties from transformation too: + +```typescript +import { classToPlain } from "class-transformer/mod.ts"; +let photo = classToPlain(photo, { excludePrefixes: ["_"] }); +``` + +This will skip all properties that start with `_` prefix. You can pass any +number of prefixes and all properties that begin with these prefixes will be +ignored. For example: + +```typescript +import { Expose } from "class-transformer/mod.ts"; + +export class User { + id: number; + private _firstName: string; + private _lastName: string; + _password: string; + + setName(firstName: string, lastName: string) { + this._firstName = firstName; + this._lastName = lastName; + } + + @Expose() + get name() { + return this.firstName + " " + this.lastName; + } +} + +const user = new User(); +user.id = 1; +user.setName("Johny", "Cage"); +user._password = 123; + +const plainUser = classToPlain(user, { excludePrefixes: ["_"] }); +// here plainUser will be equal to +// { id: 1, name: "Johny Cage" } +``` + +## Using groups to control excluded properties + +You can use groups to control what data will be exposed and what will not be: + +```typescript +import { Exclude, Expose } from "class-transformer/mod.ts"; + +@Exclude() +export class User { + id: number; + + name: string; + + @Expose({ groups: ["user", "admin"] }) // this means that this data will be exposed only to users and admins + email: string; + + @Expose({ groups: ["user"] }) // this means that this data will be exposed only to users + password: string; +} +``` + +```typescript +import { classToPlain } from "class-transformer/mod.ts"; +let user1 = classToPlain(user, { groups: ["user"] }); // will contain id, name, email and password +let user2 = classToPlain(user, { groups: ["admin"] }); // will contain id, name and email +``` + +## Using versioning to control exposed and excluded properties + +If you are building an API that has different versions, class-transformer has +extremely useful tools for that. You can control which properties of your model +should be exposed or excluded in what version. Example: + +```typescript +import { Exclude, Expose } from "class-transformer/mod.ts"; + +@Exclude() +export class User { + id: number; + + name: string; + + @Expose({ since: 0.7, until: 1 }) // this means that this property will be exposed for version starting from 0.7 until 1 + email: string; + + @Expose({ since: 2.1 }) // this means that this property will be exposed for version starting from 2.1 + password: string; +} +``` + +```typescript +import { classToPlain } from "class-transformer/mod.ts"; +let user1 = classToPlain(user, { version: 0.5 }); // will contain id and name +let user2 = classToPlain(user, { version: 0.7 }); // will contain id, name and email +let user3 = classToPlain(user, { version: 1 }); // will contain id and name +let user4 = classToPlain(user, { version: 2 }); // will contain id and name +let user5 = classToPlain(user, { version: 2.1 }); // will contain id, name nad password +``` + +## Сonverting date strings into Date objects + +Sometimes you have a Date in your plain javascript object received in a string +format. And you want to create a real javascript Date object from it. You can do +it simply by passing a Date object to the `@Type` decorator: + +```typescript +import { Type } from "class-transformer/mod.ts"; + +export class User { + id: number; + + email: string; + + password: string; + + @Type(() => Date) + registrationDate: Date; +} +``` + +Note, that dates will be converted to strings when you'll try to convert class +object to plain object. + +Same technique can be used with `Number`, `String`, `Boolean` primitive types +when you want to convert your values into these types. + +## Working with arrays + +When you are using arrays you must provide a type of the object that array +contains. This type, you specify in a `@Type()` decorator: + +```typescript +import { Type } from "class-transformer/mod.ts"; + +export class Photo { + id: number; + + name: string; + + @Type(() => Album) + albums: Album[]; +} +``` + +You can also use custom array types: + +```typescript +import { Type } from "class-transformer/mod.ts"; + +export class AlbumCollection extends Array { + // custom array functions ... +} + +export class Photo { + id: number; + + name: string; + + @Type(() => Album) + albums: AlbumCollection; +} +``` + +Library will handle proper transformation automatically. + +ES6 collections `Set` and `Map` also require the `@Type` decorator: + +```typescript +export class Skill { + name: string; +} + +export class Weapon { + name: string; + range: number; +} + +export class Player { + name: string; + + @Type(() => Skill) + skills: Set; + + @Type(() => Weapon) + weapons: Map; +} +``` + +## Additional data transformation + +### Basic usage + +You can perform additional data transformation using `@Transform` decorator. For +example, you want to make your `Date` object to be a `moment` object when you +are transforming object from plain to class: + +```typescript +import { Transform } from "class-transformer/mod.ts"; +import * as moment from "https://dev.jspm.io/npm:moment"; +import { Moment } from "https://dev.jspm.io/npm:moment"; + +export class Photo { + id: number; + + @Type(() => Date) + @Transform((value) => moment(value), { toClassOnly: true }) + date: Moment; +} +``` + +Now when you call `plainToClass` and send a plain representation of the Photo +object, it will convert a date value in your photo object to moment date. +`@Transform` decorator also supports groups and versioning. + +### Advanced usage + +The `@Transform` decorator is given more arguments to let you configure how you +want the transformation to be done. + +``` +@Transform((value, obj, type) => value) +``` + +| Argument | Description | +| -------- | --------------------------------------------- | +| `value` | The property value before the transformation. | +| `obj` | The transformation source object. | +| `type` | The transformation type. | + +## Other decorators + +| Signature | Example | Description | +| ------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------- | +| `@TransformClassToPlain` | `@TransformClassToPlain({ groups: ["user"] })` | Transform the method return with classToPlain and expose the properties on the class. | +| `@TransformClassToClass` | `@TransformClassToClass({ groups: ["user"] })` | Transform the method return with classToClass and expose the properties on the class. | +| `@TransformPlainToClass` | `@TransformPlainToClass(User, { groups: ["user"] })` | Transform the method return with plainToClass and expose the properties on the class. | + +The above decorators accept one optional argument: ClassTransformOptions - The +transform options like groups, version, name + +An example: + +```typescript +@Exclude() +class User { + id: number; + + @Expose() + firstName: string; + + @Expose() + lastName: string; + + @Expose({ groups: ["user.email"] }) + email: string; + + password: string; +} + +class UserController { + @TransformClassToPlain({ groups: ["user.email"] }) + getUser() { + const user = new User(); + user.firstName = "Snir"; + user.lastName = "Segal"; + user.password = "imnosuperman"; + + return user; + } +} + +const controller = new UserController(); +const user = controller.getUser(); +``` + +the `user` variable will contain only firstName,lastName, email properties +becuase they are the exposed variables. email property is also exposed becuase +we metioned the group "user.email". + +## Working with generics + +Generics are not supported because TypeScript does not have good reflection +abilities yet. Once TypeScript team provide us better runtime type reflection +tools, generics will be implemented. There are some tweaks however you can use, +that maybe can solve your problem. +[Checkout this example.](https://github.com/pleerock/class-transformer/tree/master/sample/sample4-generics) + +## Implicit type conversion + +> **NOTE** If you use class-validator together with class-transformer you +> propably DON'T want to enable this function. + +Enables automatic conversion between built-in types based on type information +provided by Typescript. Disabled by default. + +```ts +import { IsString } from "class-validator/mod.ts"; + +class MyPayload { + @IsString() + prop: string; +} + +const result1 = plainToClass( + MyPayload, + { prop: 1234 }, + { enableImplicitConversion: true } +); +const result2 = plainToClass( + MyPayload, + { prop: 1234 }, + { enableImplicitConversion: false } +); + +/** + * result1 will be `{ prop: "1234" }` - notice how the prop value has been converted to string. + * result2 will be `{ prop: 1234 }` - default behaviour + */ +``` + +## How does it handle circular references? + +Circular references are ignored. For example, if you are transforming class +`User` that contains property `photos` with type of `Photo`, and `Photo` +contains link `user` to its parent `User`, then `user` will be ignored during +transformation. Circular references are not ignored only during `classToClass` +operation. + +## Example with Angular2 + +Lets say you want to download users and want them automatically to be mapped to +the instances of `User` class. + +```typescript +import { plainToClass } from "class-transformer/mod.ts"; + +this.http + .get("users.json") + .map((res) => res.json()) + .map((res) => plainToClass(User, res as Object[])) + .subscribe((users) => { + // now "users" is type of User[] and each user has getName() and isAdult() methods available + console.log(users); + }); +``` + +You can also inject a class `ClassTransformer` as a service in `providers`, and +use its methods. + +## Release notes + +See information about breaking changes and release notes +[here](https://github.com/pleerock/class-transformer/tree/master/doc/release-notes.md). diff --git a/scripts.json b/scripts.json new file mode 100644 index 0000000..dfc8c06 --- /dev/null +++ b/scripts.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://deno.land/x/denon@2.4.7/schema.json", + "scripts": { + "start": { + "cmd": "deno run app.ts", + "desc": "run my app.ts file" + }, + "dev": { + "cmd": "deno run example.ts", + "desc": "run my app.ts file", + "watch": true, + "allow": ["env"] + } + }, + "tsconfig": "tsconfig.json" +} diff --git a/src/ClassTransformOptions.ts b/src/ClassTransformOptions.ts index 41abc42..b6a6423 100644 --- a/src/ClassTransformOptions.ts +++ b/src/ClassTransformOptions.ts @@ -4,13 +4,13 @@ */ export interface TargetMap { /** - * Target which Types are being specified. - */ + * Target which Types are being specified. + */ target: Function; /** - * List of properties and their Types. - */ + * List of properties and their Types. + */ properties: { [key: string]: Function }; } @@ -19,56 +19,56 @@ export interface TargetMap { */ export interface ClassTransformOptions { /** - * Exclusion strategy. By default exposeAll is used, which means that it will expose all properties are transformed - * by default. - */ + * Exclusion strategy. By default exposeAll is used, which means that it will expose all properties are transformed + * by default. + */ strategy?: "excludeAll" | "exposeAll"; /** - * Indicates if extraneous properties should be excluded from the value when converting a plain value to a class. - */ + * Indicates if extraneous properties should be excluded from the value when converting a plain value to a class. + */ excludeExtraneousValues?: boolean; /** - * Only properties with given groups gonna be transformed. - */ + * Only properties with given groups gonna be transformed. + */ groups?: string[]; /** - * Only properties with "since" > version < "until" gonna be transformed. - */ + * Only properties with "since" > version < "until" gonna be transformed. + */ version?: number; /** - * Excludes properties with the given prefixes. For example, if you mark your private properties with "_" and "__" - * you can set this option's value to ["_", "__"] and all private properties will be skipped. - * This works only for "exposeAll" strategy. - */ + * Excludes properties with the given prefixes. For example, if you mark your private properties with "_" and "__" + * you can set this option's value to ["_", "__"] and all private properties will be skipped. + * This works only for "exposeAll" strategy. + */ excludePrefixes?: string[]; /** - * If set to true then class transformer will ignore all @Expose and @Exclude decorators and what inside them. - * This option is useful if you want to kinda clone your object but do not apply decorators affects. - */ + * If set to true then class transformer will ignore all @Expose and @Exclude decorators and what inside them. + * This option is useful if you want to kinda clone your object but do not apply decorators affects. + */ ignoreDecorators?: boolean; /** - * Target maps allows to set a Types of the transforming object without using @Type decorator. - * This is useful when you are transforming external classes, or if you already have type metadata for - * objects and you don't want to set it up again. - */ + * Target maps allows to set a Types of the transforming object without using @Type decorator. + * This is useful when you are transforming external classes, or if you already have type metadata for + * objects and you don't want to set it up again. + */ targetMaps?: TargetMap[]; /** - * If set to true then class transformer will perform a circular check. (circular check is turned off by default) - * This option is useful when you know for sure that your types might have a circular dependency. - */ + * If set to true then class transformer will perform a circular check. (circular check is turned off by default) + * This option is useful when you know for sure that your types might have a circular dependency. + */ enableCircularCheck?: boolean; /** - * If set to true then class transformer will try to convert properties implicitly to their target type based on their typing information. - * - * DEFAULT: `false` - */ + * If set to true then class transformer will try to convert properties implicitly to their target type based on their typing information. + * + * DEFAULT: `false` + */ enableImplicitConversion?: boolean; } diff --git a/src/ClassTransformer.ts b/src/ClassTransformer.ts index e8b3d53..90c0fe5 100644 --- a/src/ClassTransformer.ts +++ b/src/ClassTransformer.ts @@ -1,7 +1,7 @@ import { ClassTransformOptions } from "./ClassTransformOptions.ts"; import { - TransformOperationExecutor, TransformationType, + TransformOperationExecutor, } from "./TransformOperationExecutor.ts"; export type ClassType = { diff --git a/src/TransformOperationExecutor.ts b/src/TransformOperationExecutor.ts index b4ecd85..c8f762c 100644 --- a/src/TransformOperationExecutor.ts +++ b/src/TransformOperationExecutor.ts @@ -2,10 +2,10 @@ import { Buffer } from "https://deno.land/std/node/buffer.ts"; import { ClassTransformOptions } from "./ClassTransformOptions.ts"; import { defaultMetadataStorage } from "./storage.ts"; import { + Discriminator, + JsonSubType, TypeHelpOptions, TypeOptions, - JsonSubType, - Discriminator, } from "./metadata/ExposeExcludeOptions.ts"; import { TypeMetadata } from "./metadata/TypeMetadata.ts"; @@ -46,7 +46,7 @@ export class TransformOperationExecutor { ) { if (Array.isArray(value) || value instanceof Set) { const newValue = arrayType && - this.transformationType === TransformationType.PLAIN_TO_CLASS + this.transformationType === TransformationType.PLAIN_TO_CLASS ? instantiateArrayType(arrayType) : []; diff --git a/src/decorators.ts b/src/decorators.ts index 82c693d..89b9710 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -3,11 +3,11 @@ import { defaultMetadataStorage } from "./storage.ts"; import { TypeMetadata } from "./metadata/TypeMetadata.ts"; import { ExposeMetadata } from "./metadata/ExposeMetadata.ts"; import { - ExposeOptions, + Discriminator, ExcludeOptions, - TypeHelpOptions, + ExposeOptions, TransformOptions, - Discriminator, + TypeHelpOptions, TypeOptions, } from "./metadata/ExposeExcludeOptions.ts"; import { ExcludeMetadata } from "./metadata/ExcludeMetadata.ts"; diff --git a/src/index.ts b/src/index.ts index 408056c..1717b21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,8 @@ if (!("getMetadata" in Reflect)) { - const { bold, bgRed, white } = await import( - "https://deno.land/std/fmt/colors.ts" - ); - - const fmt = (text: string) => bold(bgRed(white(text.toUpperCase()))); - console.log( - "\n", - fmt(`!!! import reflect-metadata into your main file !!!`), - `\n--> import "https://raw.githubusercontent.com/rbuckton/reflect-metadata/v0.1.12/Reflect.js"`, - "\n", + `!!! import reflect-metadata into your main file !!!`, + `\n--> import "https://esm.sh/reflect-metadata";`, + `\n it checks for 'process.env.REFLECT_METADATA_USE_MAP_POLYFILL' so need --allow-env flag`, ); } @@ -17,9 +10,25 @@ import { ClassTransformer, ClassType } from "./ClassTransformer.ts"; import { ClassTransformOptions } from "./ClassTransformOptions.ts"; export { ClassTransformer } from "./ClassTransformer.ts"; -export { ClassTransformOptions } from "./ClassTransformOptions.ts"; -export * from "./metadata/ExposeExcludeOptions.ts"; -export * from "./decorators.ts"; +export type { ClassTransformOptions } from "./ClassTransformOptions.ts"; +export type { + Discriminator, + ExcludeOptions, + ExposeOptions, + JsonSubType, + TransformOptions, + TypeHelpOptions, + TypeOptions, +} from "./metadata/ExposeExcludeOptions.ts"; +export { + Exclude, + Expose, + Transform, + TransformClassToClass, + TransformClassToPlain, + TransformPlainToClass, + Type, +} from "./decorators.ts"; const classTransformer = new ClassTransformer(); diff --git a/src/metadata/TypeMetadata.ts b/src/metadata/TypeMetadata.ts index f3ce660..b3b2723 100644 --- a/src/metadata/TypeMetadata.ts +++ b/src/metadata/TypeMetadata.ts @@ -1,6 +1,6 @@ import { - TypeHelpOptions, Discriminator, + TypeHelpOptions, TypeOptions, } from "./ExposeExcludeOptions.ts"; diff --git a/tsconfig.json b/tsconfig.json index 8e95171..fba4400 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ -{ - "compilerOptions": { - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "noImplicitAny": true - } -} +{ + "compilerOptions": { + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false + } +} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index a3626fe..0000000 --- a/tslint.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": true, - "no-trailing-whitespace": false, - "no-var-keyword": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "quotemark": [ - true, - "double" - ], - "semicolon": true, - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": [ - true, - "ban-keywords" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} \ No newline at end of file