Full Code of typestack/class-transformer for AI

develop a2734a5d154c cached
96 files
279.1 KB
72.6k tokens
276 symbols
1 requests
Download .txt
Showing preview only (303K chars total). Download the full file or copy to clipboard to get everything.
Repository: typestack/class-transformer
Branch: develop
Commit: a2734a5d154c
Files: 96
Total size: 279.1 KB

Directory structure:
gitextract_zd9snzjs/

├── .eslintrc.yml
├── .gitbook.yaml
├── .github/
│   ├── dependabot.yml
│   ├── semantic.yml
│   └── workflows/
│       ├── auto-approve-dependabot-workflow.yml
│       ├── continuous-deployment-workflow.yml
│       ├── continuous-integration-workflow.yml
│       └── lock-closed-issues-workflow.yml
├── .gitignore
├── .prettierrc.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── codecov.yml
├── docs/
│   ├── SUMMARY.md
│   └── pages/
│       ├── 01-getting-started.md
│       └── 02-basic-usage.md
├── jest.config.js
├── package.json
├── rollup.config.js
├── sample/
│   ├── sample1-simple-usage/
│   │   ├── Album.ts
│   │   ├── Photo.ts
│   │   ├── User.ts
│   │   └── app.ts
│   ├── sample2-iheritance/
│   │   ├── Album.ts
│   │   ├── Authorable.ts
│   │   ├── Photo.ts
│   │   ├── User.ts
│   │   └── app.ts
│   ├── sample3-custom-arrays/
│   │   ├── Album.ts
│   │   ├── AlbumArray.ts
│   │   ├── Photo.ts
│   │   └── app.ts
│   ├── sample4-generics/
│   │   ├── SimpleCollection.ts
│   │   ├── SuperCollection.ts
│   │   ├── User.ts
│   │   └── app.ts
│   └── sample5-custom-transformer/
│       ├── User.ts
│       └── app.ts
├── src/
│   ├── ClassTransformer.ts
│   ├── MetadataStorage.ts
│   ├── TransformOperationExecutor.ts
│   ├── constants/
│   │   └── default-options.constant.ts
│   ├── decorators/
│   │   ├── exclude.decorator.ts
│   │   ├── expose.decorator.ts
│   │   ├── index.ts
│   │   ├── transform-instance-to-instance.decorator.ts
│   │   ├── transform-instance-to-plain.decorator.ts
│   │   ├── transform-plain-to-instance.decorator.ts
│   │   ├── transform.decorator.ts
│   │   └── type.decorator.ts
│   ├── enums/
│   │   ├── index.ts
│   │   └── transformation-type.enum.ts
│   ├── index.ts
│   ├── interfaces/
│   │   ├── class-constructor.type.ts
│   │   ├── class-transformer-options.interface.ts
│   │   ├── decorator-options/
│   │   │   ├── exclude-options.interface.ts
│   │   │   ├── expose-options.interface.ts
│   │   │   ├── transform-options.interface.ts
│   │   │   ├── type-discriminator-descriptor.interface.ts
│   │   │   └── type-options.interface.ts
│   │   ├── index.ts
│   │   ├── metadata/
│   │   │   ├── exclude-metadata.interface.ts
│   │   │   ├── expose-metadata.interface.ts
│   │   │   ├── transform-fn-params.interface.ts
│   │   │   ├── transform-metadata.interface.ts
│   │   │   └── type-metadata.interface.ts
│   │   ├── target-map.interface.ts
│   │   └── type-help-options.interface.ts
│   ├── storage.ts
│   └── utils/
│       ├── get-global.util.spect.ts
│       ├── get-global.util.ts
│       ├── index.ts
│       └── is-promise.util.ts
├── test/
│   └── functional/
│       ├── basic-functionality.spec.ts
│       ├── circular-reference-problem.spec.ts
│       ├── custom-transform.spec.ts
│       ├── default-values.spec.ts
│       ├── es6-data-types.spec.ts
│       ├── ignore-decorators.spec.ts
│       ├── implicit-type-declarations.spec.ts
│       ├── inheritence.spec.ts
│       ├── prevent-array-bomb.spec.ts
│       ├── promise-field.spec.ts
│       ├── serialization-deserialization.spec.ts
│       ├── specify-maps.spec.ts
│       ├── transformation-option.spec.ts
│       ├── transformer-method.spec.ts
│       └── transformer-order.spec.ts
├── tsconfig.json
├── tsconfig.prod.cjs.json
├── tsconfig.prod.esm2015.json
├── tsconfig.prod.esm5.json
├── tsconfig.prod.json
├── tsconfig.prod.types.json
└── tsconfig.spec.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.yml
================================================
parser: '@typescript-eslint/parser'
plugins:
  - '@typescript-eslint'
parserOptions:
  ecmaVersion: 2018
  sourceType: module
  project: 
    - ./tsconfig.json
    - ./tsconfig.spec.json
extends:
  - 'plugin:@typescript-eslint/recommended'
  - 'plugin:@typescript-eslint/recommended-requiring-type-checking'
  - 'plugin:jest/recommended'
  - 'prettier'
rules:
  '@typescript-eslint/explicit-member-accessibility': off
  '@typescript-eslint/no-angle-bracket-type-assertion': off
  '@typescript-eslint/no-parameter-properties': off
  '@typescript-eslint/explicit-function-return-type': off
  '@typescript-eslint/member-delimiter-style': off
  '@typescript-eslint/no-inferrable-types': off
  '@typescript-eslint/no-explicit-any': off
  '@typescript-eslint/member-ordering': 'error'
  '@typescript-eslint/no-unused-vars':
    - 'error'
    - args: 'none'
  # TODO: Remove these and fixed issues once we merged all the current PRs. 
  '@typescript-eslint/ban-types': off 
  '@typescript-eslint/no-unsafe-return': off
  '@typescript-eslint/no-unsafe-assignment': off
  '@typescript-eslint/no-unsafe-call': off
  '@typescript-eslint/no-unsafe-member-access': off
  '@typescript-eslint/explicit-module-boundary-types': off

================================================
FILE: .gitbook.yaml
================================================
root: ./docs
​structure:  
  readme: pages/01-getting-started.md  
  summary: SUMMARY.md​

================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: npm
  directory: "/"
  schedule:
    interval: daily
    time: "10:00"
    timezone: Europe/Budapest
  open-pull-requests-limit: 0
  versioning-strategy: increase
  commit-message:
    prefix: build
    include: scope
  ignore:
    - dependency-name: "husky"


================================================
FILE: .github/semantic.yml
================================================
titleAndCommits: true
allowMergeCommits: false
scopes:
  - deps
  - deps-dev
types:
  - feat
  - fix
  - docs
  - style
  - refactor
  - perf
  - test
  - build
  - ci
  - chore
  - revert
  - merge


================================================
FILE: .github/workflows/auto-approve-dependabot-workflow.yml
================================================
name: Dependabot auto-merge
on:
  pull_request_target
jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]'
    steps:
    - name: 'Auto approve PR by Dependabot'
      uses: hmarr/auto-approve-action@v2.0.0
      with:
        github-token: "${{ secrets.TYPESTACK_BOT_TOKEN }}"
    - name: 'Comment merge command'
      uses: actions/github-script@v3
      with:
        github-token: ${{secrets.TYPESTACK_BOT_TOKEN }}
        script: |
          await github.issues.createComment({
            owner: context.repo.owner,
            repo: context.repo.repo,
            issue_number: context.issue.number,
            body: '@dependabot squash and merge'
          })

================================================
FILE: .github/workflows/continuous-deployment-workflow.yml
================================================
name: CD
on:
  release:
    types: [created]
jobs:
  publish:
    name: Publish to NPM
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1
        with:
          registry-url: https://registry.npmjs.org
      - run: npm ci --ignore-scripts
      - run: npm run prettier:check
      - run: npm run lint:check
      - run: npm run test:ci
      - run: npm run build:es2015
      - run: npm run build:esm5
      - run: npm run build:cjs
      - run: npm run build:umd
      - run: npm run build:types
      - run: cp LICENSE build/LICENSE
      - run: cp README.md build/README.md
      - run: jq 'del(.devDependencies) | del(.scripts)' package.json > build/package.json
      - run: npm publish ./build
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}


================================================
FILE: .github/workflows/continuous-integration-workflow.yml
================================================
name: CI
on: [push, pull_request]
jobs:
  checks:
    name: Linters
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1
      - run: npm ci --ignore-scripts
      - run: npm run prettier:check
      - run: npm run lint:check
  tests:
    name: Tests
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['10.x', '12.x', '14.x']
      fail-fast: false
    steps:
      - uses: actions/checkout@v1
      - name: Setting up Node.js (v${{ matrix.node-version }}.x)
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci --ignore-scripts
      - run: npm run test:ci
      - run: npm install codecov -g
        if: ${{ matrix.node-version == '14.x' }}
      - run: codecov -f ./coverage/clover.xml -t ${{ secrets.CODECOV_TOKEN }} --commit=$GITHUB_SHA --branch=${GITHUB_REF##*/}
        if: ${{ matrix.node-version == '14.x' }}
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1
      - run: npm ci --ignore-scripts
      - run: npm run build:es2015
      - run: npm run build:esm5
      - run: npm run build:cjs
      - run: npm run build:umd
      - run: npm run build:types


================================================
FILE: .github/workflows/lock-closed-issues-workflow.yml
================================================
name: 'Lock inactive threads'
on:
  schedule:
    - cron: '0 0 * * *'
jobs:
  lock:
    name: Lock closed issues
    runs-on: ubuntu-latest
    steps:
      - uses: dessant/lock-threads@v2
        with:
          github-token: ${{ github.token }}
          issue-lock-inactive-days: 30
          pr-lock-inactive-days: 30
          issue-lock-comment: >
            This issue has been automatically locked since there
            has not been any recent activity after it was closed.
            Please open a new issue for related bugs.
          pr-lock-comment: >
            This pull request has been automatically locked since there
            has not been any recent activity after it was closed.
            Please open a new issue for related bugs.


================================================
FILE: .gitignore
================================================
# Log files
logs
*.log
*.tmp
*.tmp.*
log.txt
npm-debug.log*

# Testing output
lib-cov/**
coverage/**

# Environment files
.env

# Dependency directories
node_modules

# MacOS related files
*.DS_Store
.AppleDouble
.LSOverride
._*
UserInterfaceState.xcuserstate

# Windows related files
Thumbs.db
Desktop.ini
$RECYCLE.BIN/

# IDE - Sublime
*.sublime-project
*.sublime-workspace

# IDE - VSCode
.vscode/**
!.vscode/tasks.json
!.vscode/launch.json

# IDE - IntelliJ
.idea

# Compilation output folders
dist/
build/
tmp/
out-tsc/
temp

# Files for playing around locally
playground.ts
playground.js


================================================
FILE: .prettierrc.yml
================================================
printWidth: 120
tabWidth: 2
useTabs: false
semi: true
singleQuote: true
trailingComma: es5
bracketSpacing: true
arrowParens: avoid


================================================
FILE: CHANGELOG.md
================================================
# Changelog

_This changelog follows the [keep a changelog][keep-a-changelog]_ format to maintain a human readable changelog.

### [0.5.1][v0.5.1] [BREAKING CHANGE] - 2021-11-22

#### Changed

- re-added accidentally removed deprecated function names `classToPlain` and `plainToClass`

### [0.5.0][v0.5.0] [BREAKING CHANGE] - 2021-11-20

> **NOTE:** This version fixes a security vulnerability allowing denial of service attacks with a specially crafted request payload. Please update as soon as possible.

#### Breaking Changes

See the breaking changes from `0.4.1` release. It was accidentally released as patch version.

### [0.4.1][v0.4.1] [BREAKING CHANGE] - 2021-11-20

> **NOTE:** This version fixes a security vulnerability allowing denial of service attacks with a specially crafted request payload. Please update as soon as possible.

#### Breaking Changes

**Exported functions has been renamed**
Some of the exported functions has been renamed to better reflect what they are doing.

- `classToPlain` -> `instanceToPlain`
- `plainToClass` -> `plainToInstance`
- `classToClass` -> `instanceToInstance`

#### Fixed

- prevent unhandled error in `plaintToclass` when union-type member is undefined
- fixed a scenario when a specially crafted JS object would be parsed to Array

#### Changed

- various dev-dependencies updated

### [0.4.0][v0.4.0] [BREAKING CHANGE] - 2021-02-14

#### Breaking Changes

See the breaking changes from `0.3.2` release. It was accidentally released as patch version.

#### Added

- add option to ignore unset properties
- `group` information is exposed in the `@Transform` handler
- transformation options are exposed in the `@Transform` handler

#### Fixed

- fixed TypeError when `discriminator.subTypes` is not defined

#### Changed

- various dev-dependencies has been updated

### [0.3.2][v0.3.2] [BREAKING CHANGE] - 2021-01-14

#### Breaking Changes

**Signature change for `@Transform` decorator**
From this version the `@Transform` decorator receives the transformation parameters in a a wrapper object. You need to
destructure the values you are interested in.

Old way:

```ts
@Transform((value, obj, type) => /* Do some stuff with value here. */)
```

New way with wrapper object:

```ts
@Transform(({ value, key, obj, type }) => /* Do some stuff with value here. */)
```

#### Added

- `exposeDefaultValues` option has been added, when enabled properties will use their default values when no value is present for the property
- the name of the currently transformed parameter is exposed in the `@Transform` decorator

#### Fixed

- fixed an issue with transforming `Map` (#319)
- fixed an issue with sourcemap generation (#472)

#### Changed

- various internal refactors
- various changes to the project tooling
- various dev-dependencies has been updated

### [0.3.1][v0.3.1] - 2020-07-29

#### Added

- table of content added to readme

#### Changed

- moved from Mocha to Jest
- added Prettier for code formatting
- added Eslint for linting
- updated CI configuration
- removed some unused dev dependencies
- updated dependencies to latest version

#### Fixed

- circular dependency fixed
- dev dependencies removed from package.json before publishing (no more security warnings)
- transformer order is deterministic now (#231)
- fix prototype pollution issue (#367)
- various fixes in documentation

### [0.2.3][v0.2.3] [BREAKING CHANGE]

#### Changed

- `enableImplicitConversion` has been added and imlplicit value conversion is disabled by default.
- reverted #234 - fix: write properties with defined default values on prototype which broke the `@Exclude` decorator.

### [0.2.2][v0.2.2] [BREAKING CHANGE]

> **NOTE:** This version is deprecated.

This version has introduced a breaking-change when this library is used with class-validator. See #257 for details.

#### Added

- implicity type conversion between values.

### [0.2.1][v0.2.1]

> **NOTE:** This version is deprecated.

#### Added

- add option to strip unkown properties via using the `excludeExtraneousValues` option

### [0.2.0][v0.2.0] [BREAKING CHANGE]

#### Added

- add documentation for using `Set`s and `Map`s
- add opotion to pass a discriminator function to convert values into different types based on custom conditions
- added support for polymorphism based on a named type property

#### Fixed

- fix bug when transforming `null` values as primitives

### 0.1.10

#### Fixed

- improve MetadataStorage perf by changing from Arrays to ES6 Maps by @sheiidan
- fixed getAncestor issue with unknown nested properties by @247GradLabs

### 0.1.9

#### Fixed

- objects with `null` prototype are converted properly now
- objects with unknown non primitive properties are converted properly now
- corrected a typo in the README.md
- fixed the deserialize example in the README.md

### 0.1.4

#### Added

- added `TransformClassToPlain` and `TransformClassToClass` decorators

### 0.1.0

#### Added

- renamed library from `constructor-utils` to `class-transformer`
- completely renamed most of names
- renamed all main methods: `plainToConstructor` now is `plainToClass` and `constructorToPlain` is `classToPlain`, etc.
- `plainToConstructorArray` method removed - now `plainToClass` handles it
- `@Skip()` decorator renamed to `@Exclude()`
- added `@Expose` decorator
- added lot of new options: groups, versioning, custom names, etc.
- methods and getters that should be exposed must be decorated with `@Expose` decorator
- added `excludedPrefix` to class transform options that allows exclude properties that start with one of the given prefix

### 0.0.22

#### Fixed

- fixed array with primitive types being converted

### 0.0.18-0.0.21

#### Fixed

- fixed bugs when getters are not converted with es6 target

### 0.0.17

#### Fixed

- fixed issue #4
- added type guessing during transformation from constructor to plain object
- added sample with generics

### 0.0.16

#### Changed

- renamed `constructor-utils/constructor-utils` to `constructor-utils` package namespace

### 0.0.15

#### Removed

- removed code mappings from package

### 0.0.14

#### Removed

- removed `import "reflect-metadata"` from source code. Now reflect metadata should be included like any other shims.

### 0.0.13

#### Changed

- Library has changed its name from `serializer.ts` to `constructor-utils`.
- Added `constructor-utils` namespace.

[v0.5.1]: https://github.com/typestack/class-transformer/compare/v0.5.0...v0.5.1
[v0.5.0]: https://github.com/typestack/class-transformer/compare/v0.4.1...v0.5.0
[v0.4.1]: https://github.com/typestack/class-transformer/compare/v0.4.0...v0.4.1
[v0.4.0]: https://github.com/typestack/class-transformer/compare/v0.3.2...v0.4.0
[v0.3.2]: https://github.com/typestack/class-transformer/compare/v0.3.1...v0.3.2
[v0.3.1]: https://github.com/typestack/class-transformer/compare/v0.2.3...v0.3.1
[v0.2.3]: https://github.com/typestack/class-transformer/compare/v0.2.2...v0.2.3
[v0.2.2]: https://github.com/typestack/class-transformer/compare/v0.2.1...v0.2.2
[v0.2.1]: https://github.com/typestack/class-transformer/compare/v0.2.0...v0.2.1
[v0.2.0]: https://github.com/typestack/class-transformer/compare/v0.1.10...v0.2.0
[keep-a-changelog]: https://keepachangelog.com/en/1.0.0/


================================================
FILE: LICENSE
================================================
The MIT License

Copyright (c) 2015-2020 TypeStack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.md
================================================
# class-transformer

![Build Status](https://github.com/typestack/class-transformer/workflows/CI/badge.svg)
[![codecov](https://codecov.io/gh/typestack/class-transformer/branch/develop/graph/badge.svg)](https://codecov.io/gh/typestack/class-transformer)
[![npm version](https://badge.fury.io/js/class-transformer.svg)](https://badge.fury.io/js/class-transformer)

Its ES6 and Typescript era. Nowadays you are working with classes and constructor objects more than 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.

Example how to use with angular 2 in [plunker](http://plnkr.co/edit/Mja1ZYAjVySWASMHVB9R).
Source code is available [here](https://github.com/pleerock/class-transformer-demo).

## Table of contents

- [What is class-transformer](#what-is-class-transformer)
- [Installation](#installation)
  - [Node.js](#nodejs)
  - [Browser](#browser)
- [Methods](#methods)
  - [plainToInstance](#plaintoinstance)
  - [plainToClassFromExist](#plaintoclassfromexist)
  - [instanceToPlain](#instancetoplain)
  - [instanceToInstance](#instanceToInstance)
  - [serialize](#serialize)
  - [deserialize and deserializeArray](#deserialize-and-deserializearray)
- [Enforcing type-safe instance](#enforcing-type-safe-instance)
- [Working with nested objects](#working-with-nested-objects)
  - [Providing more than one type option](#providing-more-than-one-type-option)
- [Exposing getters and method return values](#exposing-getters-and-method-return-values)
- [Exposing properties with different names](#exposing-properties-with-different-names)
- [Skipping specific properties](#skipping-specific-properties)
- [Skipping depend of operation](#skipping-depend-of-operation)
- [Skipping all properties of the class](#skipping-all-properties-of-the-class)
- [Skipping private properties, or some prefixed properties](#skipping-private-properties-or-some-prefixed-properties)
- [Using groups to control excluded properties](#using-groups-to-control-excluded-properties)
- [Using versioning to control exposed and excluded properties](#using-versioning-to-control-exposed-and-excluded-properties)
- [Сonverting date strings into Date objects](#сonverting-date-strings-into-date-objects)
- [Working with arrays](#working-with-arrays)
- [Additional data transformation](#additional-data-transformation)
  - [Basic usage](#basic-usage)
  - [Advanced usage](#advanced-usage)
- [Other decorators](#other-decorators)
- [Working with generics](#working-with-generics)
- [Implicit type conversion](#implicit-type-conversion)
- [How does it handle circular references?](#how-does-it-handle-circular-references)
- [Example with Angular2](#example-with-angular2)
- [Samples](#samples)
- [Release notes](#release-notes)

## What is class-transformer[⬆](#table-of-contents)

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 your 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 an example how it will look like:

```typescript
fetch('users.json').then((users: Object[]) => {
  const realUsers = plainToInstance(User, users);
  // now each user in realUsers is an instance of User class
});
```

Now you can use `users[0].getName()` and `users[0].isAdult()` methods.

## Installation[⬆](#table-of-contents)

### Node.js[⬆](#table-of-contents)

1. Install module:

   `npm install class-transformer --save`

2. `reflect-metadata` shim is required, install it too:

   `npm install reflect-metadata --save`

   and make sure to import it in a global place, like app.ts:

   ```typescript
   import 'reflect-metadata';
   ```

3. ES6 features are used, if you are using old version of node.js you may need to install es6-shim:

   `npm install es6-shim --save`

   and import it in a global place like app.ts:

   ```typescript
   import 'es6-shim';
   ```

### Browser[⬆](#table-of-contents)

1. Install module:

   `npm install class-transformer --save`

2. `reflect-metadata` shim is required, install it too:

   `npm install reflect-metadata --save`

   add `<script>` to reflect-metadata in the head of your `index.html`:

   ```html
   <html>
     <head>
       <!-- ... -->
       <script src="node_modules/reflect-metadata/Reflect.js"></script>
     </head>
     <!-- ... -->
   </html>
   ```

   If you are using angular 2 you should already have this shim installed.

3. If you are using system.js you may want to add this into `map` and `package` config:

   ```json
   {
     "map": {
       "class-transformer": "node_modules/class-transformer"
     },
     "packages": {
       "class-transformer": { "main": "index.js", "defaultExtension": "js" }
     }
   }
   ```

## Methods[⬆](#table-of-contents)

### plainToInstance[⬆](#table-of-contents)

This method transforms a plain javascript object to instance of specific class.

```typescript
import { plainToInstance } from 'class-transformer';

let users = plainToInstance(User, userJson); // to convert user plain object a single user. also supports arrays
```

### plainToClassFromExist[⬆](#table-of-contents)

This method transforms a plain object into an instance using an already filled Object which is an instance of 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.
```

### instanceToPlain[⬆](#table-of-contents)

This method transforms your class object back to plain javascript object, that can be `JSON.stringify` later.

```typescript
import { instanceToPlain } from 'class-transformer';
let photo = instanceToPlain(photo);
```

### instanceToInstance[⬆](#table-of-contents)

This method transforms your class object into a new instance of the class object.
This may be treated as deep clone of your objects.

```typescript
import { instanceToInstance } from 'class-transformer';
let photo = instanceToInstance(photo);
```

You can also use an `ignoreDecorators` option in transformation options to ignore all decorators your classes are using.

### serialize[⬆](#table-of-contents)

You can serialize your model right to json using `serialize` method:

```typescript
import { serialize } from 'class-transformer';
let photo = serialize(photo);
```

`serialize` works with both arrays and non-arrays.

### deserialize and deserializeArray[⬆](#table-of-contents)

You can deserialize your model from json using the `deserialize` method:

```typescript
import { deserialize } from 'class-transformer';
let photo = deserialize(Photo, photo);
```

To make deserialization work with arrays, use the `deserializeArray` method:

```typescript
import { deserializeArray } from 'class-transformer';
let photos = deserializeArray(Photo, photos);
```

## Enforcing type-safe instance[⬆](#table-of-contents)

The default behaviour of the `plainToInstance` method is to set _all_ properties from the plain object,
even those which are not specified in the class.

```typescript
import { plainToInstance } from 'class-transformer';

class User {
  id: number;
  firstName: string;
  lastName: string;
}

const fromPlainUser = {
  unkownProp: 'hello there',
  firstName: 'Umed',
  lastName: 'Khudoiberdiev',
};

console.log(plainToInstance(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 `plainToInstance` method while _exposing all your class properties_ as a requirement.

```typescript
import { Expose, plainToInstance } from 'class-transformer';

class User {
  @Expose() id: number;
  @Expose() firstName: string;
  @Expose() lastName: string;
}

const fromPlainUser = {
  unkownProp: 'hello there',
  firstName: 'Umed',
  lastName: 'Khudoiberdiev',
};

console.log(plainToInstance(User, fromPlainUser, { excludeExtraneousValues: true }));

// User {
//   id: undefined,
//   firstName: 'Umed',
//   lastName: 'Khudoiberdiev'
// }
```

## Working with nested objects[⬆](#table-of-contents)

When you are trying to transform objects that have nested objects,
it's 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, plainToInstance } from 'class-transformer';

export class Album {
  id: number;

  name: string;

  @Type(() => Photo)
  photos: Photo[];
}

export class Photo {
  id: number;
  filename: string;
}

let album = plainToInstance(Album, albumJson);
// now album is Album object with Photo objects inside
```

### Providing more than one type option[⬆](#table-of-contents)

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 subtype
name for the object and the possible `subTypes` that 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, plainToInstance } from 'class-transformer';

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 = plainToInstance(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[⬆](#table-of-contents)

You can expose what your getter or method return by setting an `@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[⬆](#table-of-contents)

If you want to expose some of the properties with a different name,
you can do that by specifying a `name` option to `@Expose` decorator:

```typescript
import { Expose } from 'class-transformer';

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[⬆](#table-of-contents)

Sometimes you want to skip some properties during transformation.
This can be done using `@Exclude` decorator:

```typescript
import { Exclude } from 'class-transformer';

export class User {
  id: number;

  email: string;

  @Exclude()
  password: string;
}
```

Now when you transform a User, the `password` property will be skipped and not be included in the transformed result.

## Skipping depend of operation[⬆](#table-of-contents)

You can control on what operation you will exclude a property. Use `toClassOnly` or `toPlainOnly` options:

```typescript
import { Exclude } from 'class-transformer';

export class User {
  id: number;

  email: string;

  @Exclude({ toPlainOnly: true })
  password: string;
}
```

Now `password` property will be excluded only during `instanceToPlain` operation. Vice versa, use the `toClassOnly` option.

## Skipping all properties of the class[⬆](#table-of-contents)

You can skip all properties of the class, and expose only those are needed explicitly:

```typescript
import { Exclude, Expose } from 'class-transformer';

@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 { instanceToPlain } from 'class-transformer';
let photo = instanceToPlain(photo, { strategy: 'excludeAll' });
```

In this case you don't need to `@Exclude()` a whole class.

## Skipping private properties, or some prefixed properties[⬆](#table-of-contents)

If you name your private properties with a prefix, lets say with `_`,
then you can exclude such properties from transformation too:

```typescript
import { instanceToPlain } from 'class-transformer';
let photo = instanceToPlain(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, instanceToPlain } from 'class-transformer';

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 = instanceToPlain(user, { excludePrefixes: ['_'] });
// here plainUser will be equal to
// { id: 1, name: "Johny Cage" }
```

## Using groups to control excluded properties[⬆](#table-of-contents)

You can use groups to control what data will be exposed and what will not be:

```typescript
import { Exclude, Expose, instanceToPlain } from 'class-transformer';

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;
}

let user1 = instanceToPlain(user, { groups: ['user'] }); // will contain id, name, email and password
let user2 = instanceToPlain(user, { groups: ['admin'] }); // will contain id, name and email
```

## Using versioning to control exposed and excluded properties[⬆](#table-of-contents)

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, instanceToPlain } from 'class-transformer';

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;
}

let user1 = instanceToPlain(user, { version: 0.5 }); // will contain id and name
let user2 = instanceToPlain(user, { version: 0.7 }); // will contain id, name and email
let user3 = instanceToPlain(user, { version: 1 }); // will contain id and name
let user4 = instanceToPlain(user, { version: 2 }); // will contain id and name
let user5 = instanceToPlain(user, { version: 2.1 }); // will contain id, name and password
```

## Сonverting date strings into Date objects[⬆](#table-of-contents)

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';

export class User {
  id: number;

  email: string;

  password: string;

  @Type(() => Date)
  registrationDate: Date;
}
```

Same technique can be used with `Number`, `String`, `Boolean`
primitive types when you want to convert your values into these types.

## Working with arrays[⬆](#table-of-contents)

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';

export class Photo {
  id: number;

  name: string;

  @Type(() => Album)
  albums: Album[];
}
```

You can also use custom array types:

```typescript
import { Type } from 'class-transformer';

export class AlbumCollection extends Array<Album> {
  // 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<Skill>;

  @Type(() => Weapon)
  weapons: Map<string, Weapon>;
}
```

## Additional data transformation[⬆](#table-of-contents)

### Basic usage[⬆](#table-of-contents)

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';
import * as moment from 'moment';
import { Moment } from 'moment';

export class Photo {
  id: number;

  @Type(() => Date)
  @Transform(({ value }) => moment(value), { toClassOnly: true })
  date: Moment;
}
```

Now when you call `plainToInstance` 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[⬆](#table-of-contents)

The `@Transform` decorator is given more arguments to let you configure how you want the transformation to be done.

```ts
@Transform(({ value, key, obj, type }) => value)
```

| Argument  | Description                                             |
| --------- | ------------------------------------------------------- |
| `value`   | The property value before the transformation.           |
| `key`     | The name of the transformed property.                   |
| `obj`     | The transformation source object.                       |
| `type`    | The transformation type.                                |
| `options` | The options object passed to the transformation method. |

## Other decorators[⬆](#table-of-contents)

| Signature                | Example                                              | Description                                                                           |
| ------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `@TransformClassToPlain` | `@TransformClassToPlain({ groups: ["user"] })`       | Transform the method return with instanceToPlain and expose the properties on the class. |
| `@TransformClassToClass` | `@TransformClassToClass({ groups: ["user"] })`       | Transform the method return with instanceToInstance and expose the properties on the class. |
| `@TransformPlainToClass` | `@TransformPlainToClass(User, { groups: ["user"] })` | Transform the method return with plainToInstance 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 because they are
the exposed variables. email property is also exposed because we metioned the group "user.email".

## Working with generics[⬆](#table-of-contents)

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[⬆](#table-of-contents)

> **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';

class MyPayload {
  @IsString()
  prop: string;
}

const result1 = plainToInstance(MyPayload, { prop: 1234 }, { enableImplicitConversion: true });
const result2 = plainToInstance(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?[⬆](#table-of-contents)

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 `instanceToInstance` operation.

## Example with Angular2[⬆](#table-of-contents)

Lets say you want to download users and want them automatically to be mapped to the instances of `User` class.

```typescript
import { plainToInstance } from 'class-transformer';

this.http
  .get('users.json')
  .map(res => res.json())
  .map(res => plainToInstance(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.

Example how to use with angular 2 in [plunker](http://plnkr.co/edit/Mja1ZYAjVySWASMHVB9R).
Source code is [here](https://github.com/pleerock/class-transformer-demo).

## Samples[⬆](#table-of-contents)

Take a look on samples in [./sample](https://github.com/pleerock/class-transformer/tree/master/sample) for more examples of
usages.

## Release notes[⬆](#table-of-contents)

See information about breaking changes and release notes [here](https://github.com/typestack/class-transformer/blob/master/CHANGELOG.md).


================================================
FILE: codecov.yml
================================================
coverage:
  range: 70..100
  round: down
  precision: 2
  status:
    project:
      default:
        threshold: 0%
        paths: 
          - src/**/*.ts
comment: off
ignore:
  - testing/**/*.ts
  - src/**/*.interface.ts


================================================
FILE: docs/SUMMARY.md
================================================
# Table of contents

- [Getting Started](pages/01-getting-started.md)
- [Basic usage](pages/02-basis-usage.md)


================================================
FILE: docs/pages/01-getting-started.md
================================================
# Getting Started

The `class-transformer` package is a zero-dependency utility library helping you to quickly transform class instances to plain objects and vice-versa.
It works well with the [`class-validator`][class-validator] library. The main features include:

- conditionally transforming object properties
- excluding specific properties from the transformed object
- exposing properties under a different name on the transformed object
- supports both NodeJS and browsers
- fully three-shakable
- zero external dependencies

## Installation

To start using class-transformer install the required packages via NPM:

```bash
npm install class-transformer reflect-metadata
```

Import the `reflect-metadata` package at the **first line** of your application:

```ts
import 'reflect-metadata';

// Your other imports and initialization code
// comes here after you imported the reflect-metadata package!
```

As the last step, you need to enable emitting decorator metadata in your Typescript config. Add these two lines to your `tsconfig.json` file under the `compilerOptions` key:

```json
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
```

Now you are ready to use class-transformer with Typescript!

## Basic Usage

The most basic usage is to transform a class to a plain object:

```ts
import { Expose, Exclude, classToInstance } from 'class-transformer';

class User {
  /**
   * When transformed to plain the `_id` property will be remapped to `id`
   * in the plain object.
   */
  @Expose({ name: 'id' })
  private _id: string;

  /**
   * Expose the `name` property as it is in the plain object.
   */
  @Expose()
  public name: string;

  /**
   * Exclude the `passwordHash` so it won't be included in the plain object.
   */
  @Exclude()
  public passwordHash: string;
}

const user = getUserMagically();
// contains: User { _id: '42', name: 'John Snow', passwordHash: '2f55ce082...' }

const plain = classToInstance(user);
// contains { id: '42', name: 'John Snow' }
```

[class-validator]: https://github.com/typestack/class-validator/


================================================
FILE: docs/pages/02-basic-usage.md
================================================
# Basic usage

There are two main exported functions what can be used for transformations:

- `plainToInstance` - transforms a plain object to an instance of the specified class constructor
- `instanceToPlain` - transforms a _known_ class instance to a plain object

Both function transforms the source object to the target via applying the metadata registered by the decorators on
the class definition. The four main decorators are:

- `@Expose` specifies how expose the given property on the plain object
- `@Exclude` marks the property as skipped, so it won't show up in the transformation
- `@Transform` allows specifying a custom transformation on the property via a custom handler
- `@Type` decorator explicitly sets the type of the property, during the transformation `class-transformer` will attempt
  to create an instance of the specified type

You must always decorate all your properties with an `@Expose` or `@Exclude` decorator.

> **NOTE:** It's important to remember `class-transformer` will call the target type with am empty constructor, so if
> you are using a type what requires special setup, you need to use a `@Transform` decorator and create the instance yourself.


================================================
FILE: jest.config.js
================================================
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  collectCoverageFrom: ['src/**/*.ts', '!src/**/index.ts', '!src/**/*.interface.ts'],
  globals: {
    'ts-jest': {
      tsconfig: 'tsconfig.spec.json',
    },
  },
};

================================================
FILE: package.json
================================================
{
  "name": "class-transformer",
  "version": "0.5.1",
  "description": "Proper decorator-based transformation / serialization / deserialization of plain javascript objects to class constructors",
  "author": "TypeStack contributors",
  "license": "MIT",
  "readmeFilename": "README.md",
  "sideEffects": false,
  "main": "./cjs/index.js",
  "module": "./esm5/index.js",
  "es2015": "./esm2015/index.js",
  "typings": "./types/index.d.ts",
  "repository": {
    "type": "git",
    "url": "https://github.com/typestack/class-transformer.git"
  },
  "tags": [
    "serialization",
    "deserialization",
    "serializer",
    "typescript",
    "object-to-class",
    "typescript-serializer"
  ],
  "scripts": {
    "build": "npm run build:cjs",
    "build:clean": "rimraf build",
    "build:es2015": "tsc --project tsconfig.prod.esm2015.json",
    "build:esm5": "tsc --project tsconfig.prod.esm5.json",
    "build:cjs": "tsc --project tsconfig.prod.cjs.json",
    "build:umd": "rollup --config rollup.config.js",
    "build:types": "tsc --project tsconfig.prod.types.json",
    "prettier:fix": "prettier --write \"**/*.{ts,md}\"",
    "prettier:check": "prettier --check \"**/*.{ts,md}\"",
    "lint:fix": "eslint --max-warnings 0 --fix --ext .ts src/",
    "lint:check": "eslint --max-warnings 0 --ext .ts src/",
    "test": "jest --coverage --verbose",
    "test:watch": "jest --watch",
    "test:ci": "jest --runInBand --no-cache --coverage --verbose"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.md": [
      "npm run prettier:fix"
    ],
    "*.ts": [
      "npm run prettier:fix"
    ]
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^25.0.5",
    "@rollup/plugin-node-resolve": "^15.2.2",
    "@types/jest": "^27.5.0",
    "@types/node": "^20.8.5",
    "@typescript-eslint/eslint-plugin": "^4.33.0",
    "@typescript-eslint/parser": "^4.33.0",
    "eslint": "^7.32.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-jest": "^26.8.7",
    "husky": "^4.3.8",
    "jest": "^26.6.3",
    "lint-staged": "^14.0.1",
    "prettier": "^2.8.8",
    "reflect-metadata": "0.1.13",
    "rimraf": "5.0.5",
    "rollup": "^2.79.1",
    "rollup-plugin-terser": "^7.0.2",
    "ts-jest": "^26.5.6",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.5"
  }
}


================================================
FILE: rollup.config.js
================================================
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';

export default {
  input: 'build/esm5/index.js',
  output: [
    {
      name: 'ClassTransformer',
      format: 'umd',
      file: 'build/bundles/class-transformer.umd.js',
      sourcemap: true,
    },
    {
      name: 'ClassTransformer',
      format: 'umd',
      file: 'build/bundles/class-transformer.umd.min.js',
      sourcemap: true,
      plugins: [terser()],
    },
  ],
  plugins: [commonjs(), nodeResolve()],
};

================================================
FILE: sample/sample1-simple-usage/Album.ts
================================================
import { Type, Exclude } from '../../src/decorators';
import { Photo } from './Photo';

export class Album {
  id: string;

  @Exclude()
  name: string;

  @Type(() => Photo)
  photos: Photo[];
}


================================================
FILE: sample/sample1-simple-usage/Photo.ts
================================================
import { Type } from '../../src/decorators';
import { Album } from './Album';
import { User } from './User';

export class Photo {
  id: string;

  filename: string;

  description: string;

  tags: string[];

  @Type(() => User)
  author: User;

  @Type(() => Album)
  albums: Album[];

  get name() {
    return this.id + '_' + this.filename;
  }

  getAlbums() {
    console.log('this is not serialized/deserialized');
    return this.albums;
  }
}


================================================
FILE: sample/sample1-simple-usage/User.ts
================================================
import { Type } from '../../src/decorators';

export class User {
  @Type(() => Number)
  id: number;

  firstName: string;

  lastName: string;

  @Type(() => Date)
  registrationDate: Date;
}


================================================
FILE: sample/sample1-simple-usage/app.ts
================================================
import 'es6-shim';
import 'reflect-metadata';
import { plainToClass, classToPlain } from '../../src/index';
import { Photo } from './Photo';

// check deserialization

let photoJson = {
  id: '1',
  filename: 'myphoto.jpg',
  description: 'about my photo',
  tags: ['me', 'iam'],
  author: {
    id: '2',
    firstName: 'Johny',
    lastName: 'Cage',
  },
  albums: [
    {
      id: '1',
      name: 'My life',
    },
    {
      id: '2',
      name: 'My young years',
    },
  ],
};

let photo = plainToClass(Photo, photoJson);
console.log('deserialized object: ', photo);

// now check serialization

let newPhotoJson = classToPlain(photo);
console.log('serialized object: ', newPhotoJson);

// try to deserialize an array
console.log('-------------------------------');

let photosJson = [
  {
    id: '1',
    filename: 'myphoto.jpg',
    description: 'about my photo',
    author: {
      id: '2',
      firstName: 'Johny',
      lastName: 'Cage',
      registrationDate: '1995-12-17T03:24:00',
    },
    albums: [
      {
        id: '1',
        name: 'My life',
      },
      {
        id: '2',
        name: 'My young years',
      },
    ],
  },
  {
    id: '2',
    filename: 'hisphoto.jpg',
    description: 'about his photo',
    author: {
      id: '2',
      firstName: 'Johny',
      lastName: 'Cage',
    },
    albums: [
      {
        id: '1',
        name: 'My life',
      },
      {
        id: '2',
        name: 'My young years',
      },
    ],
  },
];

let photos = plainToClass(Photo, photosJson);
console.log('deserialized array: ', photos);

// now check array serialization

let newPhotosJson = classToPlain(photos);
console.log('serialized array: ', newPhotosJson);


================================================
FILE: sample/sample2-iheritance/Album.ts
================================================
import { Type, Exclude } from '../../src/decorators';
import { Photo } from './Photo';
import { Authorable } from './Authorable';

export class Album extends Authorable {
  id: string;

  @Exclude()
  name: string;

  @Type(() => Photo)
  photos: Photo[];
}


================================================
FILE: sample/sample2-iheritance/Authorable.ts
================================================
import { Type, Exclude } from '../../src/decorators';
import { User } from './User';

export class Authorable {
  authorName: string;

  @Exclude()
  authorEmail: string;

  @Type(() => User)
  author: User;
}


================================================
FILE: sample/sample2-iheritance/Photo.ts
================================================
import { Type, Exclude } from '../../src/decorators';
import { Album } from './Album';
import { Authorable } from './Authorable';

export class Photo extends Authorable {
  id: string;

  filename: string;

  description: string;

  @Exclude() // this will ignore skipping inherited from Authorable class
  authorEmail: string;

  @Type(() => Album)
  albums: Album[];
}


================================================
FILE: sample/sample2-iheritance/User.ts
================================================
import { Type } from '../../src/decorators';

export class User {
  @Type(() => Number)
  id: number;

  firstName: string;

  lastName: string;

  @Type(() => Date)
  registrationDate: Date;
}


================================================
FILE: sample/sample2-iheritance/app.ts
================================================
import 'es6-shim';
import 'reflect-metadata';
import { classToPlain, plainToClass } from '../../src/index';
import { Photo } from './Photo';

let photoJson = {
  id: '1',
  filename: 'myphoto.jpg',
  description: 'about my photo',
  authorName: 'Johny.Cage',
  authorEmail: 'johny@cage.com',
  author: {
    id: '2',
    firstName: 'Johny',
    lastName: 'Cage',
  },
  albums: [
    {
      id: '1',
      authorName: 'Johny.Cage',
      authorEmail: 'johny@cage.com',
      name: 'My life',
    },
    {
      id: '2',
      authorName: 'Johny.Cage',
      authorEmail: 'johny@cage.com',
      name: 'My young years',
    },
  ],
};

let photo = plainToClass(Photo, photoJson);
console.log('deserialized object: ', photo);

// now check serialization

let newPhotoJson = classToPlain(photo);
console.log('serialized object: ', newPhotoJson);

// try to deserialize an array
console.log('-------------------------------');

let photosJson = [
  {
    id: '1',
    filename: 'myphoto.jpg',
    description: 'about my photo',
    author: {
      id: '2',
      firstName: 'Johny',
      lastName: 'Cage',
      registrationDate: '1995-12-17T03:24:00',
    },
    albums: [
      {
        id: '1',
        name: 'My life',
      },
      {
        id: '2',
        name: 'My young years',
      },
    ],
  },
  {
    id: '2',
    filename: 'hisphoto.jpg',
    description: 'about his photo',
    author: {
      id: '2',
      firstName: 'Johny',
      lastName: 'Cage',
    },
    albums: [
      {
        id: '1',
        name: 'My life',
      },
      {
        id: '2',
        name: 'My young years',
      },
    ],
  },
];

let photos = plainToClass(Photo, photosJson);
console.log('deserialized array: ', photos);

// now check array serialization

let newPhotosJson = classToPlain(photos);
console.log('serialized array: ', newPhotosJson);


================================================
FILE: sample/sample3-custom-arrays/Album.ts
================================================
export class Album {
  id: string;

  name: string;
}


================================================
FILE: sample/sample3-custom-arrays/AlbumArray.ts
================================================
import { Album } from './Album';

export class AlbumArray extends Array<Album> {
  findByName(name: string) {
    return this.find(album => album.name === name);
  }
}


================================================
FILE: sample/sample3-custom-arrays/Photo.ts
================================================
import { Album } from './Album';
import { AlbumArray } from './AlbumArray';
import { Type } from '../../src/decorators';

export class Photo {
  id: string;

  filename: string;

  description: string;

  tags: string[];

  @Type(() => Album)
  albums: AlbumArray;
}


================================================
FILE: sample/sample3-custom-arrays/app.ts
================================================
import 'es6-shim';
import 'reflect-metadata';
import { classToPlain, plainToClass } from '../../src/index';
import { Photo } from './Photo';

// check deserialization

let photoJson = {
  id: '1',
  filename: 'myphoto.jpg',
  description: 'about my photo',
  tags: ['me', 'iam'],
  albums: [
    {
      id: '1',
      name: 'My life',
    },
    {
      id: '2',
      name: 'My young years',
    },
  ],
};

let photo = plainToClass(Photo, photoJson);
console.log('deserialized object: ', photo);
console.log('-----------------------------');
console.log('Trying to find album: ', photo.albums.findByName('My life'));
console.log('-----------------------------');

// now check serialization

let newPhotoJson = classToPlain(photo);
console.log('serialized object: ', newPhotoJson);
console.log('-----------------------------');


================================================
FILE: sample/sample4-generics/SimpleCollection.ts
================================================
export class SimpleCollection<T> {
  items: T[];
  count: number;
}


================================================
FILE: sample/sample4-generics/SuperCollection.ts
================================================
import { Type, Exclude } from '../../src/decorators';

export class SuperCollection<T> {
  @Exclude()
  private type: Function;

  @Type(options => {
    return (options.newObject as SuperCollection<T>).type;
  })
  items: T[];

  count: number;

  constructor(type: Function) {
    this.type = type;
  }
}


================================================
FILE: sample/sample4-generics/User.ts
================================================
import { Exclude } from '../../src/decorators';

export class User {
  id: number;

  firstName: string;

  lastName: string;

  @Exclude()
  password: string;

  constructor(id: number, firstName: string, lastName: string, password: string) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.password = password;
  }

  get name() {
    return this.firstName + ' ' + this.lastName;
  }
}


================================================
FILE: sample/sample4-generics/app.ts
================================================
import 'es6-shim';
import 'reflect-metadata';
import { SimpleCollection } from './SimpleCollection';
import { User } from './User';
import { classToPlain, plainToClass, plainToClassFromExist } from '../../src/index';
import { SuperCollection } from './SuperCollection';

let collection = new SimpleCollection<User>();
collection.items = [new User(1, 'Johny', 'Cage', '*******'), new User(2, 'Dima', 'Cage', '*******')];
collection.count = 2;

// using generics works only for classToPlain operations, since in runtime we can
// "guess" type without type provided only we have a constructor, not plain object.

// console.log(classToPlain(collection));

// alternatively you can use factory method

let collectionJson = {
  items: [
    {
      id: 1,
      firstName: 'Johny',
      lastName: 'Cage',
      password: '*******',
    },
    {
      id: 2,
      firstName: 'Dima',
      lastName: 'Cage',
      password: '*******',
    },
  ],
};

console.log(plainToClassFromExist(new SuperCollection<User>(User), collectionJson));


================================================
FILE: sample/sample5-custom-transformer/User.ts
================================================
import { Type, Transform } from '../../src/decorators';
import * as moment from 'moment';

export class User {
  id: number;

  name: string;

  @Type(() => Date)
  @Transform(value => value.toString(), { toPlainOnly: true })
  @Transform(value => moment(value), { toClassOnly: true })
  date: Date;
}


================================================
FILE: sample/sample5-custom-transformer/app.ts
================================================
import 'es6-shim';
import 'reflect-metadata';
import { plainToClass, classToPlain } from '../../src/index';
import { User } from './User';

let userJson = {
  id: 1,
  name: 'Johny Cage',
  date: new Date().valueOf(),
};

console.log(plainToClass(User, userJson));

const user = new User();
user.id = 1;
user.name = 'Johny Cage';
user.date = new Date();

console.log(classToPlain(user));


================================================
FILE: src/ClassTransformer.ts
================================================
import { ClassTransformOptions } from './interfaces';
import { TransformOperationExecutor } from './TransformOperationExecutor';
import { TransformationType } from './enums';
import { ClassConstructor } from './interfaces';
import { defaultOptions } from './constants/default-options.constant';

export class ClassTransformer {
  // -------------------------------------------------------------------------
  // Public Methods
  // -------------------------------------------------------------------------

  /**
   * Converts class (constructor) object to plain (literal) object. Also works with arrays.
   */
  instanceToPlain<T extends Record<string, any>>(object: T, options?: ClassTransformOptions): Record<string, any>;
  instanceToPlain<T extends Record<string, any>>(object: T[], options?: ClassTransformOptions): Record<string, any>[];
  instanceToPlain<T extends Record<string, any>>(
    object: T | T[],
    options?: ClassTransformOptions
  ): Record<string, any> | Record<string, any>[] {
    const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, {
      ...defaultOptions,
      ...options,
    });
    return executor.transform(undefined, object, undefined, undefined, undefined, undefined);
  }

  /**
   * Converts class (constructor) object to plain (literal) object.
   * Uses given plain object as source object (it means fills given plain object with data from class object).
   * Also works with arrays.
   */
  classToPlainFromExist<T extends Record<string, any>, P>(
    object: T,
    plainObject: P,
    options?: ClassTransformOptions
  ): T;
  classToPlainFromExist<T extends Record<string, any>, P>(
    object: T,
    plainObjects: P[],
    options?: ClassTransformOptions
  ): T[];
  classToPlainFromExist<T extends Record<string, any>, P>(
    object: T,
    plainObject: P | P[],
    options?: ClassTransformOptions
  ): T | T[] {
    const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, {
      ...defaultOptions,
      ...options,
    });
    return executor.transform(plainObject, object, undefined, undefined, undefined, undefined);
  }

  /**
   * Converts plain (literal) object to class (constructor) object. Also works with arrays.
   */
  plainToInstance<T extends Record<string, any>, V extends Array<any>>(
    cls: ClassConstructor<T>,
    plain: V,
    options?: ClassTransformOptions
  ): T[];
  plainToInstance<T extends Record<string, any>, V>(
    cls: ClassConstructor<T>,
    plain: V,
    options?: ClassTransformOptions
  ): T;
  plainToInstance<T extends Record<string, any>, V>(
    cls: ClassConstructor<T>,
    plain: V | V[],
    options?: ClassTransformOptions
  ): T | T[] {
    const executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, {
      ...defaultOptions,
      ...options,
    });
    return executor.transform(undefined, plain, cls, undefined, undefined, undefined);
  }

  /**
   * Converts plain (literal) object to class (constructor) object.
   * Uses given object as source object (it means fills given object with data from plain object).
   * Also works with arrays.
   */
  plainToClassFromExist<T extends Record<string, any>, V extends Array<any>>(
    clsObject: T,
    plain: V,
    options?: ClassTransformOptions
  ): T;
  plainToClassFromExist<T extends Record<string, any>, V>(clsObject: T, plain: V, options?: ClassTransformOptions): T[];
  plainToClassFromExist<T extends Record<string, any>, V>(
    clsObject: T,
    plain: V | V[],
    options?: ClassTransformOptions
  ): T | T[] {
    const executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, {
      ...defaultOptions,
      ...options,
    });
    return executor.transform(clsObject, plain, undefined, undefined, undefined, undefined);
  }

  /**
   * Converts class (constructor) object to new class (constructor) object. Also works with arrays.
   */
  instanceToInstance<T>(object: T, options?: ClassTransformOptions): T;
  instanceToInstance<T>(object: T[], options?: ClassTransformOptions): T[];
  instanceToInstance<T>(object: T | T[], options?: ClassTransformOptions): T | T[] {
    const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, {
      ...defaultOptions,
      ...options,
    });
    return executor.transform(undefined, object, undefined, undefined, undefined, undefined);
  }

  /**
   * Converts class (constructor) object to plain (literal) object.
   * Uses given plain object as source object (it means fills given plain object with data from class object).
   * Also works with arrays.
   */
  classToClassFromExist<T>(object: T, fromObject: T, options?: ClassTransformOptions): T;
  classToClassFromExist<T>(object: T, fromObjects: T[], options?: ClassTransformOptions): T[];
  classToClassFromExist<T>(object: T, fromObject: T | T[], options?: ClassTransformOptions): T | T[] {
    const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, {
      ...defaultOptions,
      ...options,
    });
    return executor.transform(fromObject, object, undefined, undefined, undefined, undefined);
  }

  /**
   * Serializes given object to a JSON string.
   */
  serialize<T>(object: T, options?: ClassTransformOptions): string;
  serialize<T>(object: T[], options?: ClassTransformOptions): string;
  serialize<T>(object: T | T[], options?: ClassTransformOptions): string {
    return JSON.stringify(this.instanceToPlain(object, options));
  }

  /**
   * Deserializes given JSON string to a object of the given class.
   */
  deserialize<T>(cls: ClassConstructor<T>, json: string, options?: ClassTransformOptions): T {
    const jsonObject: T = JSON.parse(json);
    return this.plainToInstance(cls, jsonObject, options);
  }

  /**
   * Deserializes given JSON string to an array of objects of the given class.
   */
  deserializeArray<T>(cls: ClassConstructor<T>, json: string, options?: ClassTransformOptions): T[] {
    const jsonObject: any[] = JSON.parse(json);
    return this.plainToInstance(cls, jsonObject, options);
  }
}


================================================
FILE: src/MetadataStorage.ts
================================================
import { TypeMetadata, ExposeMetadata, ExcludeMetadata, TransformMetadata } from './interfaces';
import { TransformationType } from './enums';

/**
 * Storage all library metadata.
 */
export class MetadataStorage {
  // -------------------------------------------------------------------------
  // Properties
  // -------------------------------------------------------------------------

  private _typeMetadatas = new Map<Function, Map<string, TypeMetadata>>();
  private _transformMetadatas = new Map<Function, Map<string, TransformMetadata[]>>();
  private _exposeMetadatas = new Map<Function, Map<string, ExposeMetadata>>();
  private _excludeMetadatas = new Map<Function, Map<string, ExcludeMetadata>>();
  private _ancestorsMap = new Map<Function, Function[]>();

  // -------------------------------------------------------------------------
  // Adder Methods
  // -------------------------------------------------------------------------

  addTypeMetadata(metadata: TypeMetadata): void {
    if (!this._typeMetadatas.has(metadata.target)) {
      this._typeMetadatas.set(metadata.target, new Map<string, TypeMetadata>());
    }
    this._typeMetadatas.get(metadata.target).set(metadata.propertyName, metadata);
  }

  addTransformMetadata(metadata: TransformMetadata): void {
    if (!this._transformMetadatas.has(metadata.target)) {
      this._transformMetadatas.set(metadata.target, new Map<string, TransformMetadata[]>());
    }
    if (!this._transformMetadatas.get(metadata.target).has(metadata.propertyName)) {
      this._transformMetadatas.get(metadata.target).set(metadata.propertyName, []);
    }
    this._transformMetadatas.get(metadata.target).get(metadata.propertyName).push(metadata);
  }

  addExposeMetadata(metadata: ExposeMetadata): void {
    if (!this._exposeMetadatas.has(metadata.target)) {
      this._exposeMetadatas.set(metadata.target, new Map<string, ExposeMetadata>());
    }
    this._exposeMetadatas.get(metadata.target).set(metadata.propertyName, metadata);
  }

  addExcludeMetadata(metadata: ExcludeMetadata): void {
    if (!this._excludeMetadatas.has(metadata.target)) {
      this._excludeMetadatas.set(metadata.target, new Map<string, ExcludeMetadata>());
    }
    this._excludeMetadatas.get(metadata.target).set(metadata.propertyName, metadata);
  }

  // -------------------------------------------------------------------------
  // Public Methods
  // -------------------------------------------------------------------------

  findTransformMetadatas(
    target: Function,
    propertyName: string,
    transformationType: TransformationType
  ): TransformMetadata[] {
    return this.findMetadatas(this._transformMetadatas, target, propertyName).filter(metadata => {
      if (!metadata.options) return true;
      if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true;

      if (metadata.options.toClassOnly === true) {
        return (
          transformationType === TransformationType.CLASS_TO_CLASS ||
          transformationType === TransformationType.PLAIN_TO_CLASS
        );
      }
      if (metadata.options.toPlainOnly === true) {
        return transformationType === TransformationType.CLASS_TO_PLAIN;
      }

      return true;
    });
  }

  findExcludeMetadata(target: Function, propertyName: string): ExcludeMetadata {
    return this.findMetadata(this._excludeMetadatas, target, propertyName);
  }

  findExposeMetadata(target: Function, propertyName: string): ExposeMetadata {
    return this.findMetadata(this._exposeMetadatas, target, propertyName);
  }

  findExposeMetadataByCustomName(target: Function, name: string): ExposeMetadata {
    return this.getExposedMetadatas(target).find(metadata => {
      return metadata.options && metadata.options.name === name;
    });
  }

  findTypeMetadata(target: Function, propertyName: string): TypeMetadata {
    return this.findMetadata(this._typeMetadatas, target, propertyName);
  }

  getStrategy(target: Function): 'excludeAll' | 'exposeAll' | 'none' {
    const excludeMap = this._excludeMetadatas.get(target);
    const exclude = excludeMap && excludeMap.get(undefined);
    const exposeMap = this._exposeMetadatas.get(target);
    const expose = exposeMap && exposeMap.get(undefined);
    if ((exclude && expose) || (!exclude && !expose)) return 'none';
    return exclude ? 'excludeAll' : 'exposeAll';
  }

  getExposedMetadatas(target: Function): ExposeMetadata[] {
    return this.getMetadata(this._exposeMetadatas, target);
  }

  getExcludedMetadatas(target: Function): ExcludeMetadata[] {
    return this.getMetadata(this._excludeMetadatas, target);
  }

  getExposedProperties(target: Function, transformationType: TransformationType): string[] {
    return this.getExposedMetadatas(target)
      .filter(metadata => {
        if (!metadata.options) return true;
        if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true;

        if (metadata.options.toClassOnly === true) {
          return (
            transformationType === TransformationType.CLASS_TO_CLASS ||
            transformationType === TransformationType.PLAIN_TO_CLASS
          );
        }
        if (metadata.options.toPlainOnly === true) {
          return transformationType === TransformationType.CLASS_TO_PLAIN;
        }

        return true;
      })
      .map(metadata => metadata.propertyName);
  }

  getExcludedProperties(target: Function, transformationType: TransformationType): string[] {
    return this.getExcludedMetadatas(target)
      .filter(metadata => {
        if (!metadata.options) return true;
        if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true;

        if (metadata.options.toClassOnly === true) {
          return (
            transformationType === TransformationType.CLASS_TO_CLASS ||
            transformationType === TransformationType.PLAIN_TO_CLASS
          );
        }
        if (metadata.options.toPlainOnly === true) {
          return transformationType === TransformationType.CLASS_TO_PLAIN;
        }

        return true;
      })
      .map(metadata => metadata.propertyName);
  }

  clear(): void {
    this._typeMetadatas.clear();
    this._exposeMetadatas.clear();
    this._excludeMetadatas.clear();
    this._ancestorsMap.clear();
  }

  // -------------------------------------------------------------------------
  // Private Methods
  // -------------------------------------------------------------------------

  private getMetadata<T extends { target: Function; propertyName: string }>(
    metadatas: Map<Function, Map<string, T>>,
    target: Function
  ): T[] {
    const metadataFromTargetMap = metadatas.get(target);
    let metadataFromTarget: T[];
    if (metadataFromTargetMap) {
      metadataFromTarget = Array.from(metadataFromTargetMap.values()).filter(meta => meta.propertyName !== undefined);
    }
    const metadataFromAncestors: T[] = [];
    for (const ancestor of this.getAncestors(target)) {
      const ancestorMetadataMap = metadatas.get(ancestor);
      if (ancestorMetadataMap) {
        const metadataFromAncestor = Array.from(ancestorMetadataMap.values()).filter(
          meta => meta.propertyName !== undefined
        );
        metadataFromAncestors.push(...metadataFromAncestor);
      }
    }
    return metadataFromAncestors.concat(metadataFromTarget || []);
  }

  private findMetadata<T extends { target: Function; propertyName: string }>(
    metadatas: Map<Function, Map<string, T>>,
    target: Function,
    propertyName: string
  ): T {
    const metadataFromTargetMap = metadatas.get(target);
    if (metadataFromTargetMap) {
      const metadataFromTarget = metadataFromTargetMap.get(propertyName);
      if (metadataFromTarget) {
        return metadataFromTarget;
      }
    }
    for (const ancestor of this.getAncestors(target)) {
      const ancestorMetadataMap = metadatas.get(ancestor);
      if (ancestorMetadataMap) {
        const ancestorResult = ancestorMetadataMap.get(propertyName);
        if (ancestorResult) {
          return ancestorResult;
        }
      }
    }
    return undefined;
  }

  private findMetadatas<T extends { target: Function; propertyName: string }>(
    metadatas: Map<Function, Map<string, T[]>>,
    target: Function,
    propertyName: string
  ): T[] {
    const metadataFromTargetMap = metadatas.get(target);
    let metadataFromTarget: T[];
    if (metadataFromTargetMap) {
      metadataFromTarget = metadataFromTargetMap.get(propertyName);
    }
    const metadataFromAncestorsTarget: T[] = [];
    for (const ancestor of this.getAncestors(target)) {
      const ancestorMetadataMap = metadatas.get(ancestor);
      if (ancestorMetadataMap) {
        if (ancestorMetadataMap.has(propertyName)) {
          metadataFromAncestorsTarget.push(...ancestorMetadataMap.get(propertyName));
        }
      }
    }
    return metadataFromAncestorsTarget
      .slice()
      .reverse()
      .concat((metadataFromTarget || []).slice().reverse());
  }

  private getAncestors(target: Function): Function[] {
    if (!target) return [];
    if (!this._ancestorsMap.has(target)) {
      const ancestors: Function[] = [];
      for (
        let baseClass = Object.getPrototypeOf(target.prototype.constructor);
        typeof baseClass.prototype !== 'undefined';
        baseClass = Object.getPrototypeOf(baseClass.prototype.constructor)
      ) {
        ancestors.push(baseClass);
      }
      this._ancestorsMap.set(target, ancestors);
    }
    return this._ancestorsMap.get(target);
  }
}


================================================
FILE: src/TransformOperationExecutor.ts
================================================
import { defaultMetadataStorage } from './storage';
import { ClassTransformOptions, TypeHelpOptions, TypeMetadata, TypeOptions } from './interfaces';
import { TransformationType } from './enums';
import { getGlobal, isPromise } from './utils';

function instantiateArrayType(arrayType: Function): Array<any> | Set<any> {
  const array = new (arrayType as any)();
  if (!(array instanceof Set) && !('push' in array)) {
    return [];
  }
  return array;
}

export class TransformOperationExecutor {
  // -------------------------------------------------------------------------
  // Private Properties
  // -------------------------------------------------------------------------

  private recursionStack = new Set<Record<string, any>>();

  // -------------------------------------------------------------------------
  // Constructor
  // -------------------------------------------------------------------------

  constructor(private transformationType: TransformationType, private options: ClassTransformOptions) {}

  // -------------------------------------------------------------------------
  // Public Methods
  // -------------------------------------------------------------------------

  transform(
    source: Record<string, any> | Record<string, any>[] | any,
    value: Record<string, any> | Record<string, any>[] | any,
    targetType: Function | TypeMetadata,
    arrayType: Function,
    isMap: boolean,
    level: number = 0
  ): any {
    if (Array.isArray(value) || value instanceof Set) {
      const newValue =
        arrayType && this.transformationType === TransformationType.PLAIN_TO_CLASS
          ? instantiateArrayType(arrayType)
          : [];
      (value as any[]).forEach((subValue, index) => {
        const subSource = source ? source[index] : undefined;
        if (!this.options.enableCircularCheck || !this.isCircular(subValue)) {
          let realTargetType;
          if (
            typeof targetType !== 'function' &&
            targetType &&
            targetType.options &&
            targetType.options.discriminator &&
            targetType.options.discriminator.property &&
            targetType.options.discriminator.subTypes
          ) {
            if (this.transformationType === TransformationType.PLAIN_TO_CLASS) {
              realTargetType = targetType.options.discriminator.subTypes.find(
                subType =>
                  subType.name === subValue[(targetType as { options: TypeOptions }).options.discriminator.property]
              );
              const options: TypeHelpOptions = { newObject: newValue, object: subValue, property: undefined };
              const newType = targetType.typeFunction(options);
              realTargetType === undefined ? (realTargetType = newType) : (realTargetType = realTargetType.value);
              if (!targetType.options.keepDiscriminatorProperty)
                delete subValue[targetType.options.discriminator.property];
            }

            if (this.transformationType === TransformationType.CLASS_TO_CLASS) {
              realTargetType = subValue.constructor;
            }
            if (this.transformationType === TransformationType.CLASS_TO_PLAIN) {
              subValue[targetType.options.discriminator.property] = targetType.options.discriminator.subTypes.find(
                subType => subType.value === subValue.constructor
              ).name;
            }
          } else {
            realTargetType = targetType;
          }
          const value = this.transform(
            subSource,
            subValue,
            realTargetType,
            undefined,
            subValue instanceof Map,
            level + 1
          );

          if (newValue instanceof Set) {
            newValue.add(value);
          } else {
            newValue.push(value);
          }
        } else if (this.transformationType === TransformationType.CLASS_TO_CLASS) {
          if (newValue instanceof Set) {
            newValue.add(subValue);
          } else {
            newValue.push(subValue);
          }
        }
      });
      return newValue;
    } else if (targetType === String && !isMap) {
      if (value === null || value === undefined) return value;
      return String(value);
    } else if (targetType === Number && !isMap) {
      if (value === null || value === undefined) return value;
      return Number(value);
    } else if (targetType === Boolean && !isMap) {
      if (value === null || value === undefined) return value;
      return Boolean(value);
    } else if ((targetType === Date || value instanceof Date) && !isMap) {
      if (value instanceof Date) {
        return new Date(value.valueOf());
      }
      if (value === null || value === undefined) return value;
      return new Date(value);
    } else if (!!getGlobal().Buffer && (targetType === Buffer || value instanceof Buffer) && !isMap) {
      if (value === null || value === undefined) return value;
      return Buffer.from(value);
    } else if (isPromise(value) && !isMap) {
      return new Promise((resolve, reject) => {
        value.then(
          (data: any) => resolve(this.transform(undefined, data, targetType, undefined, undefined, level + 1)),
          reject
        );
      });
    } else if (!isMap && value !== null && typeof value === 'object' && typeof value.then === 'function') {
      // Note: We should not enter this, as promise has been handled above
      // This option simply returns the Promise preventing a JS error from happening and should be an inaccessible path.
      return value; // skip promise transformation
    } else if (typeof value === 'object' && value !== null) {
      // try to guess the type
      if (!targetType && value.constructor !== Object /* && TransformationType === TransformationType.CLASS_TO_PLAIN*/)
        if (!Array.isArray(value) && value.constructor === Array) {
          // Somebody attempts to convert special Array like object to Array, eg:
          // const evilObject = { '100000000': '100000000', __proto__: [] };
          // This could be used to cause Denial-of-service attack so we don't allow it.
          // See prevent-array-bomb.spec.ts for more details.
        } else {
          // We are good we can use the built-in constructor
          targetType = value.constructor;
        }
      if (!targetType && source) targetType = source.constructor;

      if (this.options.enableCircularCheck) {
        // add transformed type to prevent circular references
        this.recursionStack.add(value);
      }

      const keys = this.getKeys(targetType as Function, value, isMap);
      let newValue: any = source ? source : {};
      if (
        !source &&
        (this.transformationType === TransformationType.PLAIN_TO_CLASS ||
          this.transformationType === TransformationType.CLASS_TO_CLASS)
      ) {
        if (isMap) {
          newValue = new Map();
        } else if (targetType) {
          newValue = new (targetType as any)();
        } else {
          newValue = {};
        }
      }

      // traverse over keys
      for (const key of keys) {
        if (key === '__proto__' || key === 'constructor') {
          continue;
        }

        const valueKey = key;
        let newValueKey = key,
          propertyName = key;
        if (!this.options.ignoreDecorators && targetType) {
          if (this.transformationType === TransformationType.PLAIN_TO_CLASS) {
            const exposeMetadata = defaultMetadataStorage.findExposeMetadataByCustomName(targetType as Function, key);
            if (exposeMetadata) {
              propertyName = exposeMetadata.propertyName;
              newValueKey = exposeMetadata.propertyName;
            }
          } else if (
            this.transformationType === TransformationType.CLASS_TO_PLAIN ||
            this.transformationType === TransformationType.CLASS_TO_CLASS
          ) {
            const exposeMetadata = defaultMetadataStorage.findExposeMetadata(targetType as Function, key);
            if (exposeMetadata && exposeMetadata.options && exposeMetadata.options.name) {
              newValueKey = exposeMetadata.options.name;
            }
          }
        }

        // get a subvalue
        let subValue: any = undefined;
        if (this.transformationType === TransformationType.PLAIN_TO_CLASS) {
          /**
           * This section is added for the following report:
           * https://github.com/typestack/class-transformer/issues/596
           *
           * We should not call functions or constructors when transforming to class.
           */
          subValue = value[valueKey];
        } else {
          if (value instanceof Map) {
            subValue = value.get(valueKey);
          } else if (value[valueKey] instanceof Function) {
            subValue = value[valueKey]();
          } else {
            subValue = value[valueKey];
          }
        }

        // determine a type
        let type: any = undefined,
          isSubValueMap = subValue instanceof Map;
        if (targetType && isMap) {
          type = targetType;
        } else if (targetType) {
          const metadata = defaultMetadataStorage.findTypeMetadata(targetType as Function, propertyName);
          if (metadata) {
            const options: TypeHelpOptions = { newObject: newValue, object: value, property: propertyName };
            const newType = metadata.typeFunction ? metadata.typeFunction(options) : metadata.reflectedType;
            if (
              metadata.options &&
              metadata.options.discriminator &&
              metadata.options.discriminator.property &&
              metadata.options.discriminator.subTypes
            ) {
              if (!(value[valueKey] instanceof Array)) {
                if (this.transformationType === TransformationType.PLAIN_TO_CLASS) {
                  type = metadata.options.discriminator.subTypes.find(subType => {
                    if (subValue && subValue instanceof Object && metadata.options.discriminator.property in subValue) {
                      return subType.name === subValue[metadata.options.discriminator.property];
                    }
                  });
                  type === undefined ? (type = newType) : (type = type.value);
                  if (!metadata.options.keepDiscriminatorProperty) {
                    if (subValue && subValue instanceof Object && metadata.options.discriminator.property in subValue) {
                      delete subValue[metadata.options.discriminator.property];
                    }
                  }
                }
                if (this.transformationType === TransformationType.CLASS_TO_CLASS) {
                  type = subValue.constructor;
                }
                if (this.transformationType === TransformationType.CLASS_TO_PLAIN) {
                  if (subValue) {
                    subValue[metadata.options.discriminator.property] = metadata.options.discriminator.subTypes.find(
                      subType => subType.value === subValue.constructor
                    ).name;
                  }
                }
              } else {
                type = metadata;
              }
            } else {
              type = newType;
            }
            isSubValueMap = isSubValueMap || metadata.reflectedType === Map;
          } else if (this.options.targetMaps) {
            // try to find a type in target maps
            this.options.targetMaps
              .filter(map => map.target === targetType && !!map.properties[propertyName])
              .forEach(map => (type = map.properties[propertyName]));
          } else if (
            this.options.enableImplicitConversion &&
            this.transformationType === TransformationType.PLAIN_TO_CLASS
          ) {
            // if we have no registererd type via the @Type() decorator then we check if we have any
            // type declarations in reflect-metadata (type declaration is emited only if some decorator is added to the property.)
            const reflectedType = (Reflect as any).getMetadata(
              'design:type',
              (targetType as Function).prototype,
              propertyName
            );

            if (reflectedType) {
              type = reflectedType;
            }
          }
        }

        // if value is an array try to get its custom array type
        const arrayType = Array.isArray(value[valueKey])
          ? this.getReflectedType(targetType as Function, propertyName)
          : undefined;

        // const subValueKey = TransformationType === TransformationType.PLAIN_TO_CLASS && newKeyName ? newKeyName : key;
        const subSource = source ? source[valueKey] : undefined;

        // if its deserialization then type if required
        // if we uncomment this types like string[] will not work
        // if (this.transformationType === TransformationType.PLAIN_TO_CLASS && !type && subValue instanceof Object && !(subValue instanceof Date))
        //     throw new Error(`Cannot determine type for ${(targetType as any).name }.${propertyName}, did you forget to specify a @Type?`);

        // if newValue is a source object that has method that match newKeyName then skip it
        if (newValue.constructor.prototype) {
          const descriptor = this.getPropertyDescriptor(newValue.constructor.prototype, newValueKey);
          if (
            (this.transformationType === TransformationType.PLAIN_TO_CLASS ||
              this.transformationType === TransformationType.CLASS_TO_CLASS) &&
            // eslint-disable-next-line @typescript-eslint/unbound-method
            ((descriptor && !descriptor.set) || newValue[newValueKey] instanceof Function)
          )
            //  || TransformationType === TransformationType.CLASS_TO_CLASS
            continue;
        }

        if (!this.options.enableCircularCheck || !this.isCircular(subValue)) {
          const transformKey = this.transformationType === TransformationType.PLAIN_TO_CLASS ? newValueKey : key;
          let finalValue;

          if (this.transformationType === TransformationType.CLASS_TO_PLAIN) {
            // Get original value
            finalValue = value[transformKey];
            // Apply custom transformation
            finalValue = this.applyCustomTransformations(
              finalValue,
              targetType as Function,
              transformKey,
              value,
              this.transformationType
            );
            // If nothing change, it means no custom transformation was applied, so use the subValue.
            finalValue = value[transformKey] === finalValue ? subValue : finalValue;
            // Apply the default transformation
            finalValue = this.transform(subSource, finalValue, type, arrayType, isSubValueMap, level + 1);
          } else {
            if (subValue === undefined && this.options.exposeDefaultValues) {
              // Set default value if nothing provided
              finalValue = newValue[newValueKey];
            } else {
              finalValue = this.transform(subSource, subValue, type, arrayType, isSubValueMap, level + 1);
              finalValue = this.applyCustomTransformations(
                finalValue,
                targetType as Function,
                transformKey,
                value,
                this.transformationType
              );
            }
          }

          if (finalValue !== undefined || this.options.exposeUnsetFields) {
            if (newValue instanceof Map) {
              newValue.set(newValueKey, finalValue);
            } else {
              newValue[newValueKey] = finalValue;
            }
          }
        } else if (this.transformationType === TransformationType.CLASS_TO_CLASS) {
          let finalValue = subValue;
          finalValue = this.applyCustomTransformations(
            finalValue,
            targetType as Function,
            key,
            value,
            this.transformationType
          );
          if (finalValue !== undefined || this.options.exposeUnsetFields) {
            if (newValue instanceof Map) {
              newValue.set(newValueKey, finalValue);
            } else {
              newValue[newValueKey] = finalValue;
            }
          }
        }
      }

      if (this.options.enableCircularCheck) {
        this.recursionStack.delete(value);
      }

      return newValue;
    } else {
      return value;
    }
  }

  private applyCustomTransformations(
    value: any,
    target: Function,
    key: string,
    obj: any,
    transformationType: TransformationType
  ): boolean {
    let metadatas = defaultMetadataStorage.findTransformMetadatas(target, key, this.transformationType);

    // apply versioning options
    if (this.options.version !== undefined) {
      metadatas = metadatas.filter(metadata => {
        if (!metadata.options) return true;

        return this.checkVersion(metadata.options.since, metadata.options.until);
      });
    }

    // apply grouping options
    if (this.options.groups && this.options.groups.length) {
      metadatas = metadatas.filter(metadata => {
        if (!metadata.options) return true;

        return this.checkGroups(metadata.options.groups);
      });
    } else {
      metadatas = metadatas.filter(metadata => {
        return !metadata.options || !metadata.options.groups || !metadata.options.groups.length;
      });
    }

    metadatas.forEach(metadata => {
      value = metadata.transformFn({ value, key, obj, type: transformationType, options: this.options });
    });

    return value;
  }

  // preventing circular references
  private isCircular(object: Record<string, any>): boolean {
    return this.recursionStack.has(object);
  }

  private getReflectedType(target: Function, propertyName: string): Function | undefined {
    if (!target) return undefined;
    const meta = defaultMetadataStorage.findTypeMetadata(target, propertyName);
    return meta ? meta.reflectedType : undefined;
  }

  private getKeys(target: Function, object: Record<string, any>, isMap: boolean): string[] {
    // determine exclusion strategy
    let strategy = defaultMetadataStorage.getStrategy(target);
    if (strategy === 'none') strategy = this.options.strategy || 'exposeAll'; // exposeAll is default strategy

    // get all keys that need to expose
    let keys: any[] = [];
    if (strategy === 'exposeAll' || isMap) {
      if (object instanceof Map) {
        keys = Array.from(object.keys());
      } else {
        keys = Object.keys(object);
      }
    }

    if (isMap) {
      // expose & exclude do not apply for map keys only to fields
      return keys;
    }

    /**
     * If decorators are ignored but we don't want the extraneous values, then we use the
     * metadata to decide which property is needed, but doesn't apply the decorator effect.
     */
    if (this.options.ignoreDecorators && this.options.excludeExtraneousValues && target) {
      const exposedProperties = defaultMetadataStorage.getExposedProperties(target, this.transformationType);
      const excludedProperties = defaultMetadataStorage.getExcludedProperties(target, this.transformationType);
      keys = [...exposedProperties, ...excludedProperties];
    }

    if (!this.options.ignoreDecorators && target) {
      // add all exposed to list of keys
      let exposedProperties = defaultMetadataStorage.getExposedProperties(target, this.transformationType);
      if (this.transformationType === TransformationType.PLAIN_TO_CLASS) {
        exposedProperties = exposedProperties.map(key => {
          const exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key);
          if (exposeMetadata && exposeMetadata.options && exposeMetadata.options.name) {
            return exposeMetadata.options.name;
          }

          return key;
        });
      }
      if (this.options.excludeExtraneousValues) {
        keys = exposedProperties;
      } else {
        keys = keys.concat(exposedProperties);
      }

      // exclude excluded properties
      const excludedProperties = defaultMetadataStorage.getExcludedProperties(target, this.transformationType);
      if (excludedProperties.length > 0) {
        keys = keys.filter(key => {
          return !excludedProperties.includes(key);
        });
      }

      // apply versioning options
      if (this.options.version !== undefined) {
        keys = keys.filter(key => {
          const exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key);
          if (!exposeMetadata || !exposeMetadata.options) return true;

          return this.checkVersion(exposeMetadata.options.since, exposeMetadata.options.until);
        });
      }

      // apply grouping options
      if (this.options.groups && this.options.groups.length) {
        keys = keys.filter(key => {
          const exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key);
          if (!exposeMetadata || !exposeMetadata.options) return true;

          return this.checkGroups(exposeMetadata.options.groups);
        });
      } else {
        keys = keys.filter(key => {
          const exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key);
          return (
            !exposeMetadata ||
            !exposeMetadata.options ||
            !exposeMetadata.options.groups ||
            !exposeMetadata.options.groups.length
          );
        });
      }
    }

    // exclude prefixed properties
    if (this.options.excludePrefixes && this.options.excludePrefixes.length) {
      keys = keys.filter(key =>
        this.options.excludePrefixes.every(prefix => {
          return key.substr(0, prefix.length) !== prefix;
        })
      );
    }

    // make sure we have unique keys
    keys = keys.filter((key, index, self) => {
      return self.indexOf(key) === index;
    });

    return keys;
  }

  private checkVersion(since: number, until: number): boolean {
    let decision = true;
    if (decision && since) decision = this.options.version >= since;
    if (decision && until) decision = this.options.version < until;

    return decision;
  }

  private checkGroups(groups: string[]): boolean {
    if (!groups) return true;

    return this.options.groups.some(optionGroup => groups.includes(optionGroup));
  }

  private getPropertyDescriptor(obj: any, key: PropertyKey): PropertyDescriptor | undefined {
    const descriptor = Object.getOwnPropertyDescriptor(obj, key);
    if (descriptor) return descriptor;

    const prototype = Object.getPrototypeOf(obj);
    return prototype ? this.getPropertyDescriptor(prototype, key) : undefined;
  }
}


================================================
FILE: src/constants/default-options.constant.ts
================================================
import { ClassTransformOptions } from '../interfaces/class-transformer-options.interface';

/**
 * These are the default options used by any transformation operation.
 */
export const defaultOptions: Partial<ClassTransformOptions> = {
  enableCircularCheck: false,
  enableImplicitConversion: false,
  excludeExtraneousValues: false,
  excludePrefixes: undefined,
  exposeDefaultValues: false,
  exposeUnsetFields: true,
  groups: undefined,
  ignoreDecorators: false,
  strategy: undefined,
  targetMaps: undefined,
  version: undefined,
};


================================================
FILE: src/decorators/exclude.decorator.ts
================================================
import { defaultMetadataStorage } from '../storage';
import { ExcludeOptions } from '../interfaces';

/**
 * Marks the given class or property as excluded. By default the property is excluded in both
 * constructorToPlain and plainToConstructor transformations. It can be limited to only one direction
 * via using the `toPlainOnly` or `toClassOnly` option.
 *
 * Can be applied to class definitions and properties.
 */
export function Exclude(options: ExcludeOptions = {}): PropertyDecorator & ClassDecorator {
  /**
   * NOTE: The `propertyName` property must be marked as optional because
   * this decorator used both as a class and a property decorator and the
   * Typescript compiler will freak out if we make it mandatory as a class
   * decorator only receives one parameter.
   */
  return function (object: any, propertyName?: string | Symbol): void {
    defaultMetadataStorage.addExcludeMetadata({
      target: object instanceof Function ? object : object.constructor,
      propertyName: propertyName as string,
      options,
    });
  };
}


================================================
FILE: src/decorators/expose.decorator.ts
================================================
import { defaultMetadataStorage } from '../storage';
import { ExposeOptions } from '../interfaces';

/**
 * Marks the given class or property as included. By default the property is included in both
 * constructorToPlain and plainToConstructor transformations. It can be limited to only one direction
 * via using the `toPlainOnly` or `toClassOnly` option.
 *
 * Can be applied to class definitions and properties.
 */
export function Expose(options: ExposeOptions = {}): PropertyDecorator & ClassDecorator {
  /**
   * NOTE: The `propertyName` property must be marked as optional because
   * this decorator used both as a class and a property decorator and the
   * Typescript compiler will freak out if we make it mandatory as a class
   * decorator only receives one parameter.
   */
  return function (object: any, propertyName?: string | Symbol): void {
    defaultMetadataStorage.addExposeMetadata({
      target: object instanceof Function ? object : object.constructor,
      propertyName: propertyName as string,
      options,
    });
  };
}


================================================
FILE: src/decorators/index.ts
================================================
export * from './exclude.decorator';
export * from './expose.decorator';
export * from './transform-instance-to-instance.decorator';
export * from './transform-instance-to-plain.decorator';
export * from './transform-plain-to-instance.decorator';
export * from './transform.decorator';
export * from './type.decorator';


================================================
FILE: src/decorators/transform-instance-to-instance.decorator.ts
================================================
import { ClassTransformer } from '../ClassTransformer';
import { ClassTransformOptions } from '../interfaces';

/**
 * Return the class instance only with the exposed properties.
 *
 * Can be applied to functions and getters/setters only.
 */
export function TransformInstanceToInstance(params?: ClassTransformOptions): MethodDecorator {
  return function (target: Record<string, any>, propertyKey: string | Symbol, descriptor: PropertyDescriptor): void {
    const classTransformer: ClassTransformer = new ClassTransformer();
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]): Record<string, any> {
      const result: any = originalMethod.apply(this, args);
      const isPromise =
        !!result && (typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function';
      return isPromise
        ? result.then((data: any) => classTransformer.instanceToInstance(data, params))
        : classTransformer.instanceToInstance(result, params);
    };
  };
}


================================================
FILE: src/decorators/transform-instance-to-plain.decorator.ts
================================================
import { ClassTransformer } from '../ClassTransformer';
import { ClassTransformOptions } from '../interfaces';

/**
 * Transform the object from class to plain object and return only with the exposed properties.
 *
 * Can be applied to functions and getters/setters only.
 */
export function TransformInstanceToPlain(params?: ClassTransformOptions): MethodDecorator {
  return function (target: Record<string, any>, propertyKey: string | Symbol, descriptor: PropertyDescriptor): void {
    const classTransformer: ClassTransformer = new ClassTransformer();
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]): Record<string, any> {
      const result: any = originalMethod.apply(this, args);
      const isPromise =
        !!result && (typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function';
      return isPromise
        ? result.then((data: any) => classTransformer.instanceToPlain(data, params))
        : classTransformer.instanceToPlain(result, params);
    };
  };
}


================================================
FILE: src/decorators/transform-plain-to-instance.decorator.ts
================================================
import { ClassTransformer } from '../ClassTransformer';
import { ClassTransformOptions, ClassConstructor } from '../interfaces';

/**
 * Return the class instance only with the exposed properties.
 *
 * Can be applied to functions and getters/setters only.
 */
export function TransformPlainToInstance(
  classType: ClassConstructor<any>,
  params?: ClassTransformOptions
): MethodDecorator {
  return function (target: Record<string, any>, propertyKey: string | Symbol, descriptor: PropertyDescriptor): void {
    const classTransformer: ClassTransformer = new ClassTransformer();
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]): Record<string, any> {
      const result: any = originalMethod.apply(this, args);
      const isPromise =
        !!result && (typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function';
      return isPromise
        ? result.then((data: any) => classTransformer.plainToInstance(classType, data, params))
        : classTransformer.plainToInstance(classType, result, params);
    };
  };
}


================================================
FILE: src/decorators/transform.decorator.ts
================================================
import { defaultMetadataStorage } from '../storage';
import { TransformFnParams, TransformOptions } from '../interfaces';

/**
 * Defines a custom logic for value transformation.
 *
 * Can be applied to properties only.
 */
export function Transform(
  transformFn: (params: TransformFnParams) => any,
  options: TransformOptions = {}
): PropertyDecorator {
  return function (target: any, propertyName: string | Symbol): void {
    defaultMetadataStorage.addTransformMetadata({
      target: target.constructor,
      propertyName: propertyName as string,
      transformFn,
      options,
    });
  };
}


================================================
FILE: src/decorators/type.decorator.ts
================================================
import { defaultMetadataStorage } from '../storage';
import { TypeHelpOptions, TypeOptions } from '../interfaces';

/**
 * Specifies a type of the property.
 * The given TypeFunction can return a constructor. A discriminator can be given in the options.
 *
 * Can be applied to properties only.
 */
export function Type(
  typeFunction?: (type?: TypeHelpOptions) => Function,
  options: TypeOptions = {}
): PropertyDecorator {
  return function (target: any, propertyName: string | Symbol): void {
    const reflectedType = (Reflect as any).getMetadata('design:type', target, propertyName);
    defaultMetadataStorage.addTypeMetadata({
      target: target.constructor,
      propertyName: propertyName as string,
      reflectedType,
      typeFunction,
      options,
    });
  };
}


================================================
FILE: src/enums/index.ts
================================================
export * from './transformation-type.enum';


================================================
FILE: src/enums/transformation-type.enum.ts
================================================
export enum TransformationType {
  PLAIN_TO_CLASS,
  CLASS_TO_PLAIN,
  CLASS_TO_CLASS,
}


================================================
FILE: src/index.ts
================================================
import { ClassTransformer } from './ClassTransformer';
import { ClassTransformOptions } from './interfaces';
import { ClassConstructor } from './interfaces';

export { ClassTransformer } from './ClassTransformer';
export * from './decorators';
export * from './interfaces';
export * from './enums';

const classTransformer = new ClassTransformer();

/**
 * Converts class (constructor) object to plain (literal) object. Also works with arrays.
 *
 * @deprecated Function name changed, use the `instanceToPlain` method instead.
 */
export function classToPlain<T>(object: T, options?: ClassTransformOptions): Record<string, any>;
export function classToPlain<T>(object: T[], options?: ClassTransformOptions): Record<string, any>[];
export function classToPlain<T>(
  object: T | T[],
  options?: ClassTransformOptions
): Record<string, any> | Record<string, any>[] {
  return classTransformer.instanceToPlain(object, options);
}

/**
 * Converts class (constructor) object to plain (literal) object. Also works with arrays.
 */
export function instanceToPlain<T>(object: T, options?: ClassTransformOptions): Record<string, any>;
export function instanceToPlain<T>(object: T[], options?: ClassTransformOptions): Record<string, any>[];
export function instanceToPlain<T>(
  object: T | T[],
  options?: ClassTransformOptions
): Record<string, any> | Record<string, any>[] {
  return classTransformer.instanceToPlain(object, options);
}

/**
 * Converts class (constructor) object to plain (literal) object.
 * Uses given plain object as source object (it means fills given plain object with data from class object).
 * Also works with arrays.
 *
 * @deprecated This function is being removed.
 */
export function classToPlainFromExist<T>(
  object: T,
  plainObject: Record<string, any>,
  options?: ClassTransformOptions
): Record<string, any>;
export function classToPlainFromExist<T>(
  object: T,
  plainObjects: Record<string, any>[],
  options?: ClassTransformOptions
): Record<string, any>[];
export function classToPlainFromExist<T>(
  object: T,
  plainObject: Record<string, any> | Record<string, any>[],
  options?: ClassTransformOptions
): Record<string, any> | Record<string, any>[] {
  return classTransformer.classToPlainFromExist(object, plainObject, options);
}

/**
 * Converts plain (literal) object to class (constructor) object. Also works with arrays.
 *
 * @deprecated Function name changed, use the `plainToInstance` method instead.
 */
export function plainToClass<T, V>(cls: ClassConstructor<T>, plain: V[], options?: ClassTransformOptions): T[];
export function plainToClass<T, V>(cls: ClassConstructor<T>, plain: V, options?: ClassTransformOptions): T;
export function plainToClass<T, V>(cls: ClassConstructor<T>, plain: V | V[], options?: ClassTransformOptions): T | T[] {
  return classTransformer.plainToInstance(cls, plain as any, options);
}

/**
 * Converts plain (literal) object to class (constructor) object. Also works with arrays.
 */
export function plainToInstance<T, V>(cls: ClassConstructor<T>, plain: V[], options?: ClassTransformOptions): T[];
export function plainToInstance<T, V>(cls: ClassConstructor<T>, plain: V, options?: ClassTransformOptions): T;
export function plainToInstance<T, V>(
  cls: ClassConstructor<T>,
  plain: V | V[],
  options?: ClassTransformOptions
): T | T[] {
  return classTransformer.plainToInstance(cls, plain as any, options);
}

/**
 * Converts plain (literal) object to class (constructor) object.
 * Uses given object as source object (it means fills given object with data from plain object).
 *  Also works with arrays.
 *
 * @deprecated This function is being removed. The current implementation is incorrect as it modifies the source object.
 */
export function plainToClassFromExist<T, V>(clsObject: T[], plain: V[], options?: ClassTransformOptions): T[];
export function plainToClassFromExist<T, V>(clsObject: T, plain: V, options?: ClassTransformOptions): T;
export function plainToClassFromExist<T, V>(clsObject: T, plain: V | V[], options?: ClassTransformOptions): T | T[] {
  return classTransformer.plainToClassFromExist(clsObject, plain, options);
}

/**
 * Converts class (constructor) object to new class (constructor) object. Also works with arrays.
 */
export function instanceToInstance<T>(object: T, options?: ClassTransformOptions): T;
export function instanceToInstance<T>(object: T[], options?: ClassTransformOptions): T[];
export function instanceToInstance<T>(object: T | T[], options?: ClassTransformOptions): T | T[] {
  return classTransformer.instanceToInstance(object, options);
}

/**
 * Converts class (constructor) object to plain (literal) object.
 * Uses given plain object as source object (it means fills given plain object with data from class object).
 * Also works with arrays.
 *
 * @deprecated This function is being removed. The current implementation is incorrect as it modifies the source object.
 */
export function classToClassFromExist<T>(object: T, fromObject: T, options?: ClassTransformOptions): T;
export function classToClassFromExist<T>(object: T, fromObjects: T[], options?: ClassTransformOptions): T[];
export function classToClassFromExist<T>(object: T, fromObject: T | T[], options?: ClassTransformOptions): T | T[] {
  return classTransformer.classToClassFromExist(object, fromObject, options);
}

/**
 * Serializes given object to a JSON string.
 *
 * @deprecated This function is being removed. Please use
 * ```
 * JSON.stringify(instanceToPlain(object, options))
 * ```
 */
export function serialize<T>(object: T, options?: ClassTransformOptions): string;
export function serialize<T>(object: T[], options?: ClassTransformOptions): string;
export function serialize<T>(object: T | T[], options?: ClassTransformOptions): string {
  return classTransformer.serialize(object, options);
}

/**
 * Deserializes given JSON string to a object of the given class.
 *
 * @deprecated This function is being removed. Please use the following instead:
 * ```
 * instanceToClass(cls, JSON.parse(json), options)
 * ```
 */
export function deserialize<T>(cls: ClassConstructor<T>, json: string, options?: ClassTransformOptions): T {
  return classTransformer.deserialize(cls, json, options);
}

/**
 * Deserializes given JSON string to an array of objects of the given class.
 *
 * @deprecated This function is being removed. Please use the following instead:
 * ```
 * JSON.parse(json).map(value => instanceToClass(cls, value, options))
 * ```
 *
 */
export function deserializeArray<T>(cls: ClassConstructor<T>, json: string, options?: ClassTransformOptions): T[] {
  return classTransformer.deserializeArray(cls, json, options);
}


================================================
FILE: src/interfaces/class-constructor.type.ts
================================================
export type ClassConstructor<T> = {
  new (...args: any[]): T;
};


================================================
FILE: src/interfaces/class-transformer-options.interface.ts
================================================
import { TargetMap } from './target-map.interface';

/**
 * Options to be passed during transformation.
 */
export interface ClassTransformOptions {
  /**
   * 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.
   *
   * This option requires that each property on the target class has at least one `@Expose` or `@Exclude` decorator
   * assigned from this library.
   */
  excludeExtraneousValues?: boolean;

  /**
   * Only properties with given groups gonna be transformed.
   */
  groups?: string[];

  /**
   * 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.
   */
  excludePrefixes?: string[];

  /**
   * If set to true then class transformer will ignore the effect of all @Expose and @Exclude decorators.
   * This option is useful if you want to kinda clone your object but do not apply decorators affects.
   *
   * __NOTE:__ You may still have to add the decorators to make other options work.
   */
  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.
   */
  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.
   */
  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`
   */
  enableImplicitConversion?: boolean;

  /**
   * If set to true then class transformer will take default values for unprovided fields.
   * This is useful when you convert a plain object to a class and have an optional field with a default value.
   */
  exposeDefaultValues?: boolean;

  /**
   * When set to true, fields with `undefined` as value will be included in class to plain transformation. Otherwise
   * those fields will be omitted from the result.
   *
   * DEFAULT: `true`
   */
  exposeUnsetFields?: boolean;
}


================================================
FILE: src/interfaces/decorator-options/exclude-options.interface.ts
================================================
/**
 * Possible transformation options for the @Exclude decorator.
 */
export interface ExcludeOptions {
  /**
   * Exclude this property only when transforming from plain to class instance.
   */
  toClassOnly?: boolean;

  /**
   * Exclude this property only when transforming from class instance to plain object.
   */
  toPlainOnly?: boolean;
}


================================================
FILE: src/interfaces/decorator-options/expose-options.interface.ts
================================================
/**
 * Possible transformation options for the @Expose decorator.
 */
export interface ExposeOptions {
  /**
   * Name of property on the target object to expose the value of this property.
   */
  name?: string;

  /**
   * First version where this property should be exposed.
   *
   * Example:
   * ```ts
   * instanceToPlain(payload, { version: 1.0 });
   * ```
   */
  since?: number;

  /**
   * Last version where this property should be exposed.
   *
   * Example:
   * ```ts
   * instanceToPlain(payload, { version: 1.0 });
   * ```
   */
  until?: number;

  /**
   * List of transformation groups this property belongs to. When set,
   * the property will be exposed only when transform is called with
   * one of the groups specified.
   *
   * Example:
   * ```ts
   * instanceToPlain(payload, { groups: ['user'] });
   * ```
   */
  groups?: string[];

  /**
   * Expose this property only when transforming from plain to class instance.
   */
  toClassOnly?: boolean;

  /**
   * Expose this property only when transforming from class instance to plain object.
   */
  toPlainOnly?: boolean;
}


================================================
FILE: src/interfaces/decorator-options/transform-options.interface.ts
================================================
/**
 * Possible transformation options for the @Transform decorator.
 */
export interface TransformOptions {
  /**
   * First version where this property should be exposed.
   *
   * Example:
   * ```ts
   * instanceToPlain(payload, { version: 1.0 });
   * ```
   */
  since?: number;

  /**
   * Last version where this property should be exposed.
   *
   * Example:
   * ```ts
   * instanceToPlain(payload, { version: 1.0 });
   * ```
   */
  until?: number;

  /**
   * List of transformation groups this property belongs to. When set,
   * the property will be exposed only when transform is called with
   * one of the groups specified.
   *
   * Example:
   * ```ts
   * instanceToPlain(payload, { groups: ['user'] });
   * ```
   */
  groups?: string[];

  /**
   * Expose this property only when transforming from plain to class instance.
   */
  toClassOnly?: boolean;

  /**
   * Expose this property only when transforming from class instance to plain object.
   */
  toPlainOnly?: boolean;
}


================================================
FILE: src/interfaces/decorator-options/type-discriminator-descriptor.interface.ts
================================================
import { ClassConstructor } from '..';

/**
 * Discriminator object containing the type information to select a proper type
 * during transformation when a discriminator property is provided.
 */
export interface DiscriminatorDescriptor {
  /**
   * The name of the property which holds the type information in the received object.
   */
  property: string;
  /**
   * List of the available types. The transformer will try to lookup the object
   * with the same key as the value received in the defined discriminator property
   * and create an instance of the defined class.
   */
  subTypes: {
    /**
     * Name of the type.
     */
    name: string | number;

    /**
     * A class constructor which can be used to create the object.
     */
    value: ClassConstructor<any>;
  }[];
}


================================================
FILE: src/interfaces/decorator-options/type-options.interface.ts
================================================
import { DiscriminatorDescriptor } from './type-discriminator-descriptor.interface';

/**
 * Possible transformation options for the @Type decorator.
 */
export interface TypeOptions {
  /**
   * Optional discriminator object, when provided the property value will be
   * initialized according to the specified object.
   */
  discriminator?: DiscriminatorDescriptor;

  /**
   * Indicates whether to keep the discriminator property on the
   * transformed object or not. Disabled by default.
   *
   * @default false
   */
  keepDiscriminatorProperty?: boolean;
}


================================================
FILE: src/interfaces/index.ts
================================================
export * from './decorator-options/expose-options.interface';
export * from './decorator-options/exclude-options.interface';
export * from './decorator-options/transform-options.interface';
export * from './decorator-options/type-discriminator-descriptor.interface';
export * from './decorator-options/type-options.interface';
export * from './metadata/exclude-metadata.interface';
export * from './metadata/expose-metadata.interface';
export * from './metadata/transform-metadata.interface';
export * from './metadata/transform-fn-params.interface';
export * from './metadata/type-metadata.interface';
export * from './class-constructor.type';
export * from './class-transformer-options.interface';
export * from './target-map.interface';
export * from './type-help-options.interface';


================================================
FILE: src/interfaces/metadata/exclude-metadata.interface.ts
================================================
import { ExcludeOptions } from '..';

/**
 * This object represents metadata assigned to a property via the @Exclude decorator.
 */
export interface ExcludeMetadata {
  target: Function;

  /**
   * The property name this metadata belongs to on the target (class or property).
   *
   * Note: If the decorator is applied to a class the propertyName will be undefined.
   */
  propertyName: string | undefined;

  /**
   * Options passed to the @Exclude operator for this property.
   */
  options: ExcludeOptions;
}


================================================
FILE: src/interfaces/metadata/expose-metadata.interface.ts
================================================
import { ExposeOptions } from '..';

/**
 * This object represents metadata assigned to a property via the @Expose decorator.
 */
export interface ExposeMetadata {
  target: Function;

  /**
   * The property name this metadata belongs to on the target (class or property).
   *
   * Note: If the decorator is applied to a class the propertyName will be undefined.
   */
  propertyName: string | undefined;

  /**
   * Options passed to the @Expose operator for this property.
   */
  options: ExposeOptions;
}


================================================
FILE: src/interfaces/metadata/transform-fn-params.interface.ts
================================================
import { TransformationType } from '../../enums';
import { ClassTransformOptions } from '../class-transformer-options.interface';

export interface TransformFnParams {
  value: any;
  key: string;
  obj: any;
  type: TransformationType;
  options: ClassTransformOptions;
}


================================================
FILE: src/interfaces/metadata/transform-metadata.interface.ts
================================================
import { TransformOptions } from '..';
import { TransformFnParams } from './transform-fn-params.interface';

/**
 * This object represents metadata assigned to a property via the @Transform decorator.
 */
export interface TransformMetadata {
  target: Function;

  /**
   * The property name this metadata belongs to on the target (property only).
   */
  propertyName: string;

  /**
   * The custom transformation function provided by the user in the @Transform decorator.
   */
  transformFn: (params: TransformFnParams) => any;

  /**
   * Options passed to the @Transform operator for this property.
   */
  options: TransformOptions;
}


================================================
FILE: src/interfaces/metadata/type-metadata.interface.ts
================================================
import { TypeHelpOptions, TypeOptions } from '..';

/**
 * This object represents metadata assigned to a property via the @Type decorator.
 */
export interface TypeMetadata {
  target: Function;

  /**
   * The property name this metadata belongs to on the target (property only).
   */
  propertyName: string;

  /**
   * The type guessed from assigned Reflect metadata ('design:type')
   */
  reflectedType: any;

  /**
   * The custom function provided by the user in the @Type decorator which
   * returns the target type for the transformation.
   */
  typeFunction: (options?: TypeHelpOptions) => Function;

  /**
   * Options passed to the @Type operator for this property.
   */
  options: TypeOptions;
}


================================================
FILE: src/interfaces/target-map.interface.ts
================================================
/**
 * Allows to specify a map of Types in the object without using @Type decorator.
 * This is useful when you have external classes.
 */
export interface TargetMap {
  /**
   * Target which Types are being specified.
   */
  target: Function;

  /**
   * List of properties and their Types.
   */
  properties: { [key: string]: Function };
}


================================================
FILE: src/interfaces/type-help-options.interface.ts
================================================
// TODO: Document this interface. What does each property means?
export interface TypeHelpOptions {
  newObject: any;
  object: Record<string, any>;
  property: string;
}


================================================
FILE: src/storage.ts
================================================
import { MetadataStorage } from './MetadataStorage';
import { getGlobal } from './utils';

const globalScope = getGlobal();

/**
 * Default metadata storage is used as singleton and can be used to storage all metadatas.
 */
if (!globalScope.classTransformerMetadataStorage) {
  globalScope.classTransformerMetadataStorage = new MetadataStorage();
}

export const defaultMetadataStorage = globalScope.classTransformerMetadataStorage;


================================================
FILE: src/utils/get-global.util.spect.ts
================================================
import { getGlobal } from '.';

describe('getGlobal()', () => {
  it('should return true if Buffer is present in globalThis', () => {
    expect(getGlobal().Buffer).toBe(true);
  });

  it('should return false if Buffer is not present in globalThis', () => {
    const bufferImp = global.Buffer;
    delete global.Buffer;

    expect(getGlobal().Buffer).toBe(false);

    global.Buffer = bufferImp;
  });
});


================================================
FILE: src/utils/get-global.util.ts
================================================
/**
 * This function returns the global object across Node and browsers.
 *
 * Note: `globalThis` is the standardized approach however it has been added to
 * Node.js in version 12. We need to include this snippet until Node 12 EOL.
 */
export function getGlobal() {
  if (typeof globalThis !== 'undefined') {
    return globalThis;
  }

  if (typeof global !== 'undefined') {
    return global;
  }

  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore: Cannot find name 'window'.
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore: Cannot find name 'window'.
    return window;
  }

  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore: Cannot find name 'self'.
  if (typeof self !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore: Cannot find name 'self'.
    return self;
  }
}


================================================
FILE: src/utils/index.ts
================================================
export * from './get-global.util';
export * from './is-promise.util';


================================================
FILE: src/utils/is-promise.util.ts
================================================
export function isPromise<T>(p: any): p is Promise<T> {
  return p !== null && typeof p === 'object' && typeof p.then === 'function';
}


================================================
FILE: test/functional/basic-functionality.spec.ts
================================================
import 'reflect-metadata';
import {
  instanceToInstance,
  classToClassFromExist,
  instanceToPlain,
  classToPlainFromExist,
  plainToInstance,
  plainToClassFromExist,
} from '../../src/index';
import { defaultMetadataStorage } from '../../src/storage';
import { Exclude, Expose, Type, Transform } from '../../src/decorators';

describe('basic functionality', () => {
  it('should convert instance of the given object to plain javascript object and should expose all properties since its a default behaviour', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;
      firstName: string;
      lastName: string;
      password: string;
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    });

    const existUser = { id: 1, age: 27 };
    const plainUser2 = classToPlainFromExist(user, existUser);
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser);
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    });

    const classToClassUser = instanceToInstance(user);
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).toEqual(user);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser);
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    });
  });

  it('should exclude extraneous values if the excludeExtraneousValues option is set to true', () => {
    defaultMetadataStorage.clear();

    class User {
      @Expose() id: number;
      @Expose() firstName: string;
      @Expose() lastName: string;
    }

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      age: 12,
    };

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toHaveProperty('age');
    expect(transformedUser.id).toBeUndefined();

    const transformedUserWithoutExtra = plainToInstance(User, fromPlainUser, { excludeExtraneousValues: true });
    expect(transformedUserWithoutExtra).toBeInstanceOf(User);
    expect(transformedUserWithoutExtra).not.toHaveProperty('age');
  });

  it('should exclude extraneous values if both excludeExtraneousValues and ignoreDecorators option is set to true', () => {
    // fixes https://github.com/typestack/class-transformer/issues/533
    defaultMetadataStorage.clear();

    class ExampleClass {
      @Exclude()
      public valueOne!: number;

      @Expose()
      public valueTwo!: number;
    }

    const transformationOptions = {
      ignoreDecorators: true,
      excludeExtraneousValues: true,
    };

    const instance = plainToInstance(
      ExampleClass,
      { valueOne: 42, valueTwo: 42, extra: true, _otherExtra: true },
      transformationOptions
    );

    expect(instance).toBeInstanceOf(ExampleClass);
    expect(instance).toEqual({ valueOne: 42, valueTwo: 42 });

    (instance as any).extraProp = 'not-needed';

    expect(instanceToPlain(instance, transformationOptions)).toEqual({
      valueOne: 42,
      valueTwo: 42,
    });
  });

  it('should exclude all objects marked with @Exclude() decorator', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;
      firstName: string;
      lastName: string;
      @Exclude()
      password: string;
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser: any = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
    expect(plainUser.password).toBeUndefined();

    const existUser = { id: 1, age: 27, password: 'yayayaya' };
    const plainUser2 = classToPlainFromExist(user, existUser);
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'yayayaya',
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser);
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const classToClassUser = instanceToInstance(user);
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser);
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
  });

  it('should exclude all properties from object if whole class is marked with @Exclude() decorator', () => {
    defaultMetadataStorage.clear();

    @Exclude()
    class User {
      id: number;
      firstName: string;
      lastName: string;
      password: string;
    }

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser: any = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({});
    expect(plainUser.firstName).toBeUndefined();
    expect(plainUser.lastName).toBeUndefined();
    expect(plainUser.password).toBeUndefined();

    const existUser = { id: 1, age: 27 };
    const plainUser2 = classToPlainFromExist(user, existUser);
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({});

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser);
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
    });

    const classToClassUser = instanceToInstance(user);
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).toEqual({});

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser);
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
    });
  });

  it('should exclude all properties from object if whole class is marked with @Exclude() decorator, but include properties marked with @Expose() decorator', () => {
    defaultMetadataStorage.clear();

    @Exclude()
    class User {
      id: number;

      @Expose()
      firstName: string;

      @Expose()
      lastName: string;

      password: string;
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser: any = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
    expect(plainUser.password).toBeUndefined();

    const existUser = { id: 1, age: 27 };
    const plainUser2 = classToPlainFromExist(user, existUser);
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser);
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const classToClassUser = instanceToInstance(user);
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser);
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
  });

  it('should exclude all properties from object if its defined via transformation options, but include properties marked with @Expose() decorator', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;

      @Expose()
      firstName: string;

      @Expose()
      lastName: string;

      password: string;
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser: any = instanceToPlain(user, { strategy: 'excludeAll' });
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
    expect(plainUser.password).toBeUndefined();

    const existUser = { id: 1, age: 27 };
    const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'excludeAll' });
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser, { strategy: 'excludeAll' });
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'excludeAll' });
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const classToClassUser = instanceToInstance(user, { strategy: 'excludeAll' });
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'excludeAll' });
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
    });
  });

  it('should expose all properties from object if its defined via transformation options, but exclude properties marked with @Exclude() decorator', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;
      firstName: string;

      @Exclude()
      lastName: string;

      @Exclude()
      password: string;
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser: any = instanceToPlain(user, { strategy: 'exposeAll' });
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
    });
    expect(plainUser.lastName).toBeUndefined();
    expect(plainUser.password).toBeUndefined();

    const existUser = { id: 1, age: 27 };
    const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' });
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: 'Umed',
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser, { strategy: 'exposeAll' });
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({
      firstName: 'Umed',
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'exposeAll' });
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
    });

    const classToClassUser = instanceToInstance(user, { strategy: 'exposeAll' });
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'exposeAll' });
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
    });
  });

  it('should convert values to specific types if they are set via @Type decorator', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;

      @Type(type => String)
      firstName: string;

      @Type(type => String)
      lastName: string;

      @Type(type => Number)
      password: number;

      @Type(type => Boolean)
      isActive: boolean;

      @Type(type => Date)
      registrationDate: Date;

      @Type(type => String)
      lastVisitDate: string;

      @Type(type => Buffer)
      uuidBuffer: Buffer;

      @Type(type => String)
      nullableString?: null | string;

      @Type(type => Number)
      nullableNumber?: null | number;

      @Type(type => Boolean)
      nullableBoolean?: null | boolean;

      @Type(type => Date)
      nullableDate?: null | Date;

      @Type(type => Buffer)
      nullableBuffer?: null | Buffer;
    }

    const date = new Date();
    const user = new User();
    const uuid = Buffer.from('1234');
    user.firstName = 321 as any;
    user.lastName = 123 as any;
    user.password = '123' as any;
    user.isActive = '1' as any;
    user.registrationDate = date.toString() as any;
    user.lastVisitDate = date as any;
    user.uuidBuffer = uuid as any;
    user.nullableString = null as any;
    user.nullableNumber = null as any;
    user.nullableBoolean = null as any;
    user.nullableDate = null as any;
    user.nullableBuffer = null as any;

    const fromPlainUser = {
      firstName: 321,
      lastName: 123,
      password: '123',
      isActive: '1',
      registrationDate: date.toString(),
      lastVisitDate: date,
      uuidBuffer: uuid,
      nullableString: null as null | string,
      nullableNumber: null as null | string,
      nullableBoolean: null as null | string,
      nullableDate: null as null | string,
      nullableBuffer: null as null | string,
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;

    const plainUser: any = instanceToPlain(user, { strategy: 'exposeAll' });
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: '321',
      lastName: '123',
      password: 123,
      isActive: true,
      registrationDate: new Date(date.toString()),
      lastVisitDate: date.toString(),
      uuidBuffer: uuid,
      nullableString: null,
      nullableNumber: null,
      nullableBoolean: null,
      nullableDate: null,
      nullableBuffer: null,
    });

    const existUser = { id: 1, age: 27 };
    const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' });
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: '321',
      lastName: '123',
      password: 123,
      isActive: true,
      registrationDate: new Date(date.toString()),
      lastVisitDate: date.toString(),
      uuidBuffer: uuid,
      nullableString: null,
      nullableNumber: null,
      nullableBoolean: null,
      nullableDate: null,
      nullableBuffer: null,
    });
    expect(plainUser2).toEqual(existUser);

    const transformedUser = plainToInstance(User, fromPlainUser, { strategy: 'exposeAll' });
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser).toEqual({
      firstName: '321',
      lastName: '123',
      password: 123,
      isActive: true,
      registrationDate: new Date(date.toString()),
      lastVisitDate: date.toString(),
      uuidBuffer: uuid,
      nullableString: null,
      nullableNumber: null,
      nullableBoolean: null,
      nullableDate: null,
      nullableBuffer: null,
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'exposeAll' });
    expect(fromExistTransformedUser).toBeInstanceOf(User);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: '321',
      lastName: '123',
      password: 123,
      isActive: true,
      registrationDate: new Date(date.toString()),
      lastVisitDate: date.toString(),
      uuidBuffer: uuid,
      nullableString: null,
      nullableNumber: null,
      nullableBoolean: null,
      nullableDate: null,
      nullableBuffer: null,
    });

    const classToClassUser = instanceToInstance(user, { strategy: 'exposeAll' });
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).toEqual({
      firstName: '321',
      lastName: '123',
      password: 123,
      isActive: true,
      registrationDate: new Date(date.toString()),
      lastVisitDate: date.toString(),
      uuidBuffer: uuid,
      nullableString: null,
      nullableNumber: null,
      nullableBoolean: null,
      nullableDate: null,
      nullableBuffer: null,
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'exposeAll' });
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: '321',
      lastName: '123',
      password: 123,
      isActive: true,
      registrationDate: new Date(date.toString()),
      lastVisitDate: date.toString(),
      uuidBuffer: uuid,
      nullableString: null,
      nullableNumber: null,
      nullableBoolean: null,
      nullableDate: null,
      nullableBuffer: null,
    });
  });

  it('should transform nested objects too and make sure their decorators are used too', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;
      name: string;

      @Exclude()
      filename: string;

      uploadDate: Date;
    }

    class User {
      firstName: string;
      lastName: string;

      @Exclude()
      password: string;

      photo: Photo; // type should be automatically guessed
    }

    const photo = new Photo();
    photo.id = 1;
    photo.name = 'Me';
    photo.filename = 'iam.jpg';
    photo.uploadDate = new Date();

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';
    user.photo = photo;

    const plainUser: any = instanceToPlain(user, { strategy: 'exposeAll' });
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser.photo).not.toBeInstanceOf(Photo);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        name: 'Me',
        uploadDate: photo.uploadDate,
      },
    });
    expect(plainUser.password).toBeUndefined();
    expect(plainUser.photo.filename).toBeUndefined();
    expect(plainUser.photo.uploadDate).toEqual(photo.uploadDate);
    expect(plainUser.photo.uploadDate).not.toBe(photo.uploadDate);

    const existUser = { id: 1, age: 27, photo: { id: 2, description: 'photo' } };
    const plainUser2: any = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' });
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2.photo).not.toBeInstanceOf(Photo);
    expect(plainUser2).toEqual({
      id: 1,
      age: 27,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        name: 'Me',
        uploadDate: photo.uploadDate,
        description: 'photo',
      },
    });
    expect(plainUser2).toEqual(existUser);
    expect(plainUser2.password).toBeUndefined();
    expect(plainUser2.photo.filename).toBeUndefined();
    expect(plainUser2.photo.uploadDate).toEqual(photo.uploadDate);
    expect(plainUser2.photo.uploadDate).not.toBe(photo.uploadDate);
  });

  it('should transform nested objects too and make sure given type is used instead of automatically guessed one', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;
      name: string;

      @Exclude()
      filename: string;
    }

    class ExtendedPhoto implements Photo {
      id: number;

      @Exclude()
      name: string;

      filename: string;
    }

    class User {
      id: number;
      firstName: string;
      lastName: string;

      @Exclude()
      password: string;

      @Type(type => ExtendedPhoto) // force specific type
      photo: Photo;
    }

    const photo = new Photo();
    photo.id = 1;
    photo.name = 'Me';
    photo.filename = 'iam.jpg';

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';
    user.photo = photo;

    const plainUser: any = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'iam.jpg',
      },
    });
    expect(plainUser.password).toBeUndefined();
    expect(plainUser.photo.name).toBeUndefined();
  });

  it('should convert given plain object to class instance object', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;
      name: string;

      @Exclude()
      filename: string;

      metadata: string;
      uploadDate: Date;
    }

    class User {
      id: number;
      firstName: string;
      lastName: string;

      @Exclude()
      password: string;

      @Type(type => Photo)
      photo: Photo;
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';
    user.photo = new Photo();
    user.photo.id = 1;
    user.photo.name = 'Me';
    user.photo.filename = 'iam.jpg';
    user.photo.uploadDate = new Date();

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        name: 'Me',
        filename: 'iam.jpg',
        uploadDate: new Date(),
      },
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;
    const fromExistPhoto = new Photo();
    fromExistPhoto.metadata = 'taken by Camera';
    fromExistUser.photo = fromExistPhoto;

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    expect(transformedUser.photo).toBeInstanceOf(Photo);
    expect(transformedUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        name: 'Me',
        uploadDate: fromPlainUser.photo.uploadDate,
      },
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser);
    expect(fromExistTransformedUser).toEqual(fromExistUser);
    expect(fromExistTransformedUser.photo).toEqual(fromExistPhoto);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        name: 'Me',
        metadata: 'taken by Camera',
        uploadDate: fromPlainUser.photo.uploadDate,
      },
    });

    const classToClassUser = instanceToInstance(user);
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser.photo).toBeInstanceOf(Photo);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).not.toEqual(user.photo);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        name: 'Me',
        uploadDate: user.photo.uploadDate,
      },
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser);
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).not.toEqual(user.photo);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        name: 'Me',
        metadata: 'taken by Camera',
        uploadDate: user.photo.uploadDate,
      },
    });
  });

  it('should expose only properties that match given group', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;

      @Expose({
        groups: ['user', 'guest'],
      })
      filename: string;

      @Expose({
        groups: ['admin'],
      })
      status: number;

      metadata: string;
    }

    class User {
      id: number;
      firstName: string;

      @Expose({
        groups: ['user', 'guest'],
      })
      lastName: string;

      @Expose({
        groups: ['user'],
      })
      password: string;

      @Expose({
        groups: ['admin'],
      })
      isActive: boolean;

      @Type(type => Photo)
      photo: Photo;

      @Expose({
        groups: ['admin'],
      })
      @Type(type => Photo)
      photos: Photo[];
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';
    user.isActive = false;
    user.photo = new Photo();
    user.photo.id = 1;
    user.photo.filename = 'myphoto.jpg';
    user.photo.status = 1;
    user.photos = [user.photo];

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      isActive: false,
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
        status: 1,
      },
      photos: [
        {
          id: 1,
          filename: 'myphoto.jpg',
          status: 1,
        },
      ],
    };

    const fromExistUser = new User();
    fromExistUser.id = 1;
    fromExistUser.photo = new Photo();
    fromExistUser.photo.metadata = 'taken by Camera';

    const plainUser1: any = instanceToPlain(user);
    expect(plainUser1).not.toBeInstanceOf(User);
    expect(plainUser1).toEqual({
      firstName: 'Umed',
      photo: {
        id: 1,
      },
    });
    expect(plainUser1.lastName).toBeUndefined();
    expect(plainUser1.password).toBeUndefined();
    expect(plainUser1.isActive).toBeUndefined();

    const plainUser2: any = instanceToPlain(user, { groups: ['user'] });
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });
    expect(plainUser2.isActive).toBeUndefined();

    const transformedUser2 = plainToInstance(User, fromPlainUser, { groups: ['user'] });
    expect(transformedUser2).toBeInstanceOf(User);
    expect(transformedUser2.photo).toBeInstanceOf(Photo);
    expect(transformedUser2).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });

    const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { groups: ['user'] });
    expect(fromExistTransformedUser).toEqual(fromExistUser);
    expect(fromExistTransformedUser.photo).toEqual(fromExistUser.photo);
    expect(fromExistTransformedUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        metadata: 'taken by Camera',
        filename: 'myphoto.jpg',
      },
    });

    const classToClassUser = instanceToInstance(user, { groups: ['user'] });
    expect(classToClassUser).toBeInstanceOf(User);
    expect(classToClassUser.photo).toBeInstanceOf(Photo);
    expect(classToClassUser).not.toEqual(user);
    expect(classToClassUser).not.toEqual(user.photo);
    expect(classToClassUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });

    const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { groups: ['user'] });
    expect(classToClassFromExistUser).toBeInstanceOf(User);
    expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo);
    expect(classToClassFromExistUser).not.toEqual(user);
    expect(classToClassFromExistUser).not.toEqual(user.photo);
    expect(classToClassFromExistUser).toEqual(fromExistUser);
    expect(classToClassFromExistUser).toEqual({
      id: 1,
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        metadata: 'taken by Camera',
        filename: 'myphoto.jpg',
      },
    });

    const plainUser3: any = instanceToPlain(user, { groups: ['guest'] });
    expect(plainUser3).not.toBeInstanceOf(User);
    expect(plainUser3).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });
    expect(plainUser3.password).toBeUndefined();
    expect(plainUser3.isActive).toBeUndefined();

    const transformedUser3 = plainToInstance(User, fromPlainUser, { groups: ['guest'] });
    expect(transformedUser3).toBeInstanceOf(User);
    expect(transformedUser3.photo).toBeInstanceOf(Photo);
    expect(transformedUser3).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });

    const plainUser4: any = instanceToPlain(user, { groups: ['admin'] });
    expect(plainUser4).not.toBeInstanceOf(User);
    expect(plainUser4).toEqual({
      firstName: 'Umed',
      isActive: false,
      photo: {
        id: 1,
        status: 1,
      },
      photos: [
        {
          id: 1,
          status: 1,
        },
      ],
    });
    expect(plainUser4.lastName).toBeUndefined();
    expect(plainUser4.password).toBeUndefined();

    const transformedUser4 = plainToInstance(User, fromPlainUser, { groups: ['admin'] });
    expect(transformedUser4).toBeInstanceOf(User);
    expect(transformedUser4.photo).toBeInstanceOf(Photo);
    expect(transformedUser4.photos[0]).toBeInstanceOf(Photo);
    expect(transformedUser4).toEqual({
      firstName: 'Umed',
      isActive: false,
      photo: {
        id: 1,
        status: 1,
      },
      photos: [
        {
          id: 1,
          status: 1,
        },
      ],
    });

    const plainUser5: any = instanceToPlain(user, { groups: ['admin', 'user'] });
    expect(plainUser5).not.toBeInstanceOf(User);
    expect(plainUser5).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      isActive: false,
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
        status: 1,
      },
      photos: [
        {
          id: 1,
          filename: 'myphoto.jpg',
          status: 1,
        },
      ],
    });

    const transformedUser5 = plainToInstance(User, fromPlainUser, { groups: ['admin', 'user'] });
    expect(transformedUser5).toBeInstanceOf(User);
    expect(transformedUser5).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      isActive: false,
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
        status: 1,
      },
      photos: [
        {
          id: 1,
          filename: 'myphoto.jpg',
          status: 1,
        },
      ],
    });
  });

  it('should expose only properties that match given version', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;

      @Expose({
        since: 1.5,
        until: 2,
      })
      filename: string;

      @Expose({
        since: 2,
      })
      status: number;
    }

    class User {
      @Expose({
        since: 1,
        until: 2,
      })
      firstName: string;

      @Expose({
        since: 0.5,
      })
      lastName: string;

      @Exclude()
      password: string;

      @Type(type => Photo)
      photo: Photo;

      @Expose({
        since: 3,
      })
      @Type(type => Photo)
      photos: Photo[];
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';
    user.photo = new Photo();
    user.photo.id = 1;
    user.photo.filename = 'myphoto.jpg';
    user.photo.status = 1;
    user.photos = [user.photo];

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
        status: 1,
      },
      photos: [
        {
          id: 1,
          filename: 'myphoto.jpg',
          status: 1,
        },
      ],
    };

    const plainUser1: any = instanceToPlain(user);
    expect(plainUser1).not.toBeInstanceOf(User);
    expect(plainUser1).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
        status: 1,
      },
      photos: [
        {
          id: 1,
          filename: 'myphoto.jpg',
          status: 1,
        },
      ],
    });

    const transformedUser1 = plainToInstance(User, fromPlainUser);
    expect(transformedUser1).toBeInstanceOf(User);
    expect(transformedUser1.photo).toBeInstanceOf(Photo);
    expect(transformedUser1.photos[0]).toBeInstanceOf(Photo);
    expect(transformedUser1).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
        status: 1,
      },
      photos: [
        {
          id: 1,
          filename: 'myphoto.jpg',
          status: 1,
        },
      ],
    });

    const plainUser2: any = instanceToPlain(user, { version: 0.3 });
    expect(plainUser2).not.toBeInstanceOf(User);
    expect(plainUser2).toEqual({
      photo: {
        id: 1,
      },
    });

    const transformedUser2 = plainToInstance(User, fromPlainUser, { version: 0.3 });
    expect(transformedUser2).toBeInstanceOf(User);
    expect(transformedUser2.photo).toBeInstanceOf(Photo);
    expect(transformedUser2).toEqual({
      photo: {
        id: 1,
      },
    });

    const plainUser3: any = instanceToPlain(user, { version: 0.5 });
    expect(plainUser3).not.toBeInstanceOf(User);
    expect(plainUser3).toEqual({
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
      },
    });

    const transformedUser3 = plainToInstance(User, fromPlainUser, { version: 0.5 });
    expect(transformedUser3).toBeInstanceOf(User);
    expect(transformedUser3.photo).toBeInstanceOf(Photo);
    expect(transformedUser3).toEqual({
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
      },
    });

    const plainUser4: any = instanceToPlain(user, { version: 1 });
    expect(plainUser4).not.toBeInstanceOf(User);
    expect(plainUser4).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
      },
    });

    const transformedUser4 = plainToInstance(User, fromPlainUser, { version: 1 });
    expect(transformedUser4).toBeInstanceOf(User);
    expect(transformedUser4.photo).toBeInstanceOf(Photo);
    expect(transformedUser4).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
      },
    });

    const plainUser5: any = instanceToPlain(user, { version: 1.5 });
    expect(plainUser5).not.toBeInstanceOf(User);
    expect(plainUser5).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });

    const transformedUser5 = plainToInstance(User, fromPlainUser, { version: 1.5 });
    expect(transformedUser5).toBeInstanceOf(User);
    expect(transformedUser5.photo).toBeInstanceOf(Photo);
    expect(transformedUser5).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        filename: 'myphoto.jpg',
      },
    });

    const plainUser6: any = instanceToPlain(user, { version: 2 });
    expect(plainUser6).not.toBeInstanceOf(User);
    expect(plainUser6).toEqual({
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        status: 1,
      },
    });

    const transformedUser6 = plainToInstance(User, fromPlainUser, { version: 2 });
    expect(transformedUser6).toBeInstanceOf(User);
    expect(transformedUser6.photo).toBeInstanceOf(Photo);
    expect(transformedUser6).toEqual({
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        status: 1,
      },
    });

    const plainUser7: any = instanceToPlain(user, { version: 3 });
    expect(plainUser7).not.toBeInstanceOf(User);
    expect(plainUser7).toEqual({
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        status: 1,
      },
      photos: [
        {
          id: 1,
          status: 1,
        },
      ],
    });

    const transformedUser7 = plainToInstance(User, fromPlainUser, { version: 3 });
    expect(transformedUser7).toBeInstanceOf(User);
    expect(transformedUser7.photo).toBeInstanceOf(Photo);
    expect(transformedUser7.photos[0]).toBeInstanceOf(Photo);
    expect(transformedUser7).toEqual({
      lastName: 'Khudoiberdiev',
      photo: {
        id: 1,
        status: 1,
      },
      photos: [
        {
          id: 1,
          status: 1,
        },
      ],
    });
  });

  it('should expose method and accessors that have @Expose()', () => {
    defaultMetadataStorage.clear();

    class User {
      firstName: string;
      lastName: string;

      @Exclude()
      password: string;

      @Expose()
      get name(): string {
        return this.firstName + ' ' + this.lastName;
      }

      @Expose()
      getName(): string {
        return this.firstName + ' ' + this.lastName;
      }
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const plainUser: any = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      name: 'Umed Khudoiberdiev',
      getName: 'Umed Khudoiberdiev',
    });

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    const likeUser = new User();
    likeUser.firstName = 'Umed';
    likeUser.lastName = 'Khudoiberdiev';
    expect(transformedUser).toEqual(likeUser);
  });

  it('should expose with alternative name if its given', () => {
    defaultMetadataStorage.clear();

    class User {
      @Expose({ name: 'myName' })
      firstName: string;

      @Expose({ name: 'secondName' })
      lastName: string;

      @Exclude()
      password: string;

      @Expose()
      get name(): string {
        return this.firstName + ' ' + this.lastName;
      }

      @Expose({ name: 'fullName' })
      getName(): string {
        return this.firstName + ' ' + this.lastName;
      }
    }

    const user = new User();
    user.firstName = 'Umed';
    user.lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';

    const fromPlainUser = {
      myName: 'Umed',
      secondName: 'Khudoiberdiev',
      password: 'imnosuperman',
    };

    const plainUser: any = instanceToPlain(user);
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      myName: 'Umed',
      secondName: 'Khudoiberdiev',
      name: 'Umed Khudoiberdiev',
      fullName: 'Umed Khudoiberdiev',
    });

    const transformedUser = plainToInstance(User, fromPlainUser);
    expect(transformedUser).toBeInstanceOf(User);
    const likeUser = new User();
    likeUser.firstName = 'Umed';
    likeUser.lastName = 'Khudoiberdiev';
    expect(transformedUser).toEqual(likeUser);
  });

  it('should exclude all prefixed properties if prefix is given', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;
      $filename: string;
      status: number;
    }

    class User {
      $system: string;
      _firstName: string;
      _lastName: string;

      @Exclude()
      password: string;

      @Type(() => Photo)
      photo: Photo;

      @Expose()
      get name(): string {
        return this._firstName + ' ' + this._lastName;
      }
    }

    const user = new User();
    user.$system = '@#$%^&*token(*&^%$#@!';
    user._firstName = 'Umed';
    user._lastName = 'Khudoiberdiev';
    user.password = 'imnosuperman';
    user.photo = new Photo();
    user.photo.id = 1;
    user.photo.$filename = 'myphoto.jpg';
    user.photo.status = 1;

    const fromPlainUser = {
      $system: '@#$%^&*token(*&^%$#@!',
      _firstName: 'Khudoiberdiev',
      _lastName: 'imnosuperman',
      password: 'imnosuperman',
      photo: {
        id: 1,
        $filename: 'myphoto.jpg',
        status: 1,
      },
    };

    const plainUser: any = instanceToPlain(user, { excludePrefixes: ['_', '$'] });
    expect(plainUser).not.toBeInstanceOf(User);
    expect(plainUser).toEqual({
      name: 'Umed Khudoiberdiev',
      photo: {
        id: 1,
        status: 1,
      },
    });

    const transformedUser = plainToInstance(User, fromPlainUser, { excludePrefixes: ['_', '$'] });
    expect(transformedUser).toBeInstanceOf(User);
    const likeUser = new User();
    likeUser.photo = new Photo();
    likeUser.photo.id = 1;
    likeUser.photo.status = 1;
    expect(transformedUser).toEqual(likeUser);
  });

  it('should expose inherited method and accessors that have @Expose()', () => {
    class User {
      firstName: string;
      lastName: string;

      @Expose()
      get name() {
        return this.firstName + ' ' + this.lastName;
      }

      @Expose()
      getName() {
        return this.firstName + ' ' + this.lastName;
      }
    }
    class Programmer extends User {
      language: string;
    }

    const programmer = new Programmer();
    programmer.firstName = 'Umed';
    programmer.lastName = 'Khudoiberdiev';
    programmer.language = 'en';

    const fromPlainProgrammer = {
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      language: 'en',
    };

    const plainProgrammer: any = instanceToPlain(programmer);
    expect(plainProgrammer).not.toBeInstanceOf(Programmer);
    expect(plainProgrammer).toEqual({
      firstName: 'Umed',
      lastName: 'Khudoiberdiev',
      language: 'en',
      name: 'Umed Khudoiberdiev',
      getName: 'Umed Khudoiberdiev',
    });

    const transformedProgrammer = plainToInstance(Programmer, fromPlainProgrammer);
    expect(transformedProgrammer).toBeInstanceOf(Programmer);
    const likeProgrammer = new Programmer();
    likeProgrammer.firstName = 'Umed';
    likeProgrammer.lastName = 'Khudoiberdiev';
    likeProgrammer.language = 'en';
    expect(transformedProgrammer).toEqual(likeProgrammer);
  });

  it('should transform array', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;
      firstName: string;
      lastName: string;

      @Exclude()
      password: string;

      @Expose()
      get name(): string {
        return this.firstName + ' ' + this.lastName;
      }
    }

    const user1 = new User();
    user1.firstName = 'Umed';
    user1.lastName = 'Khudoiberdiev';
    user1.password = 'imnosuperman';

    const user2 = new User();
    user2.firstName = 'Dima';
    user2.lastName = 'Zotov';
    user2.password = 'imnomesser';

    const users = [user1, user2];

    const plainUsers: any = instanceToPlain(users);
    expect(plainUsers).toEqual([
      {
        firstName: 'Umed',
        lastName: 'Khudoiberdiev',
        name: 'Umed Khudoiberdiev',
      },
      {
        firstName: 'Dima',
        lastName: 'Zotov',
        name: 'Dima Zotov',
      },
    ]);

    const fromPlainUsers = [
      {
        firstName: 'Umed',
        lastName: 'Khudoiberdiev',
        name: 'Umed Khudoiberdiev',
      },
      {
        firstName: 'Dima',
        lastName: 'Zotov',
        name: 'Dima Zotov',
      },
    ];

    const existUsers = [
      { id: 1, age: 27 },
      { id: 2, age: 30 },
    ];
    const plainUser2 = classToPlainFromExist(users, existUsers);
    expect(plainUser2).toEqual([
      {
        id: 1,
        age: 27,
        firstName: 'Umed',
        lastName: 'Khudoiberdiev',
        name: 'Umed Khudoiberdiev',
      },
      {
        id: 2,
        age: 30,
        firstName: 'Dima',
        lastName: 'Zotov',
        name: 'Dima Zotov',
      },
    ]);

    const transformedUser = plainToInstance(User, fromPlainUsers);

    expect(transformedUser[0]).toBeInstanceOf(User);
    expect(transformedUser[1]).toBeInstanceOf(User);
    const likeUser1 = new User();
    likeUser1.firstName = 'Umed';
    likeUser1.lastName = 'Khudoiberdiev';

    const likeUser2 = new User();
    likeUser2.firstName = 'Dima';
    likeUser2.lastName = 'Zotov';
    expect(transformedUser).toEqual([likeUser1, likeUser2]);

    const classToClassUsers = instanceToInstance(users);
    expect(classToClassUsers[0]).toBeInstanceOf(User);
    expect(classToClassUsers[1]).toBeInstanceOf(User);
    expect(classToClassUsers[0]).not.toEqual(user1);
    expect(classToClassUsers[1]).not.toEqual(user1);

    const classUserLike1 = new User();
    classUserLike1.firstName = 'Umed';
    classUserLike1.lastName = 'Khudoiberdiev';

    const classUserLike2 = new User();
    classUserLike2.firstName = 'Dima';
    classUserLike2.lastName = 'Zotov';

    expect(classToClassUsers).toEqual([classUserLike1, classUserLike2]);

    const fromExistUser1 = new User();
    fromExistUser1.id = 1;

    const fromExistUser2 = new User();
    fromExistUser2.id = 2;

    const fromExistUsers = [fromExistUser1, fromExistUser2];

    const classToClassFromExistUser = classToClassFromExist(users, fromExistUsers);
    expect(classToClassFromExistUser[0]).toBeInstanceOf(User);
    expect(classToClassFromExistUser[1]).toBeInstanceOf(User);
    expect(classToClassFromExistUser[0]).not.toEqual(user1);
    expect(classToClassFromExistUser[1]).not.toEqual(user1);
    expect(classToClassFromExistUser).toEqual(fromExistUsers);

    const fromExistUserLike1 = new User();
    fromExistUserLike1.id = 1;
    fromExistUserLike1.firstName = 'Umed';
    fromExistUserLike1.lastName = 'Khudoiberdiev';

    const fromExistUserLike2 = new User();
    fromExistUserLike2.id = 2;
    fromExistUserLike2.firstName = 'Dima';
    fromExistUserLike2.lastName = 'Zotov';

    expect(classToClassFromExistUser).toEqual([fromExistUserLike1, fromExistUserLike2]);
  });

  it('should transform objects with null prototype', () => {
    class TestClass {
      prop: string;
    }

    const obj = Object.create(null);
    obj.a = 'JS FTW';

    const transformedClass = plainToInstance(TestClass, obj);
    expect(transformedClass).toBeInstanceOf(TestClass);
  });

  it('should not pollute the prototype with a `__proto__` property', () => {
    const object = JSON.parse('{"__proto__": { "admin": true }}');
    const plainObject = {};
    classToPlainFromExist(object, plainObject);
    expect((plainObject as any).admin).toEqual(undefined);
  });

  it('should not pollute the prototype with a `constructor.prototype` property', () => {
    const object = JSON.parse('{"constructor": { "prototype": { "admin": true }}}');
    const plainObject = {};
    classToPlainFromExist(object, plainObject);
    expect((plainObject as any).admin).toEqual(undefined);
  });

  it('should default union types where the plain type is an array to an array result', () => {
    class User {
      name: string;
    }

    class TestClass {
      @Type(() => User)
      usersDefined: User[] | undefined;

      @Type(() => User)
      usersUndefined: User[] | undefined;
    }

    const obj = Object.create(null);
    obj.usersDefined = [{ name: 'a-name' }];
    obj.usersUndefined = undefined;

    const transformedClass = plainToInstance(TestClass, obj as Record<string, any>);

    expect(transformedClass).toBeInstanceOf(TestClass);

    expect(transformedClass.usersDefined).toBeInstanceOf(Array);
    expect(transformedClass.usersDefined.length).toEqual(1);
    expect(transformedClass.usersDefined[0]).toBeInstanceOf(User);
    expect(transformedClass.usersDefined[0].name).toEqual('a-name');

    expect(transformedClass.usersUndefined).toBeUndefined();
  });
});


================================================
FILE: test/functional/circular-reference-problem.spec.ts
================================================
import 'reflect-metadata';
import { instanceToInstance, instanceToPlain, plainToInstance } from '../../src/index';
import { defaultMetadataStorage } from '../../src/storage';
import { TransformOperationExecutor } from '../../src/TransformOperationExecutor';

describe('circular reference problem', () => {
  it('should skip circular reference objects in instanceToPlain operation', () => {
    defaultMetadataStorage.clear();

    class Caption {
      text: string;
    }

    class Photo {
      id: number;
      filename: string;
      user: User;
      users: User[];
      caption: Caption;
    }

    class User {
      id: number;
      firstName: string;
      caption: Caption;
      photos: Photo[];
    }

    const photo1 = new Photo();
    photo1.id = 1;
    photo1.filename = 'me.jpg';

    const photo2 = new Photo();
    photo2.id = 2;
    photo2.filename = 'she.jpg';

    const caption = new Caption();
    caption.text = 'cool photo';

    const user = new User();
    user.caption = caption;
    user.firstName = 'Umed Khudoiberdiev';
    user.photos = [photo1, photo2];

    photo1.user = user;
    photo2.user = user;
    photo1.users = [user];
    photo2.users = [user];

    photo1.caption = caption;
    photo2.caption = caption;

    const plainUser = instanceToPlain(user, { enableCircularCheck: true });
    expect(plainUser).toEqual({
      firstName: 'Umed Khudoiberdiev',
      caption: { text: 'cool photo' },
      photos: [
        {
          id: 1,
          filename: 'me.jpg',
          users: [],
          caption: { text: 'cool photo' },
        },
        {
          id: 2,
          filename: 'she.jpg',
          users: [],
          caption: { text: 'cool photo' },
        },
      ],
    });
  });

  it('should not skip circular reference objects, but handle it correctly in instanceToInstance operation', () => {
    defaultMetadataStorage.clear();

    class Photo {
      id: number;
      filename: string;
      user: User;
      users: User[];
    }

    class User {
      id: number;
      firstName: string;
      photos: Photo[];
    }

    const photo1 = new Photo();
    photo1.id = 1;
    photo1.filename = 'me.jpg';

    const photo2 = new Photo();
    photo2.id = 2;
    photo2.filename = 'she.jpg';

    const user = new User();
    user.firstName = 'Umed Khudoiberdiev';
    user.photos = [photo1, photo2];

    photo1.user = user;
    photo2.user = user;
    photo1.users = [user];
    photo2.users = [user];

    const classUser = instanceToInstance(user, { enableCircularCheck: true });
    expect(classUser).not.toBe(user);
    expect(classUser).toBeInstanceOf(User);
    expect(classUser).toEqual(user);
  });

  describe('enableCircularCheck option', () => {
    class Photo {
      id: number;
      filename: string;
    }

    class User {
      id: number;
      firstName: string;
      photos: Photo[];
    }
    let isCircularSpy: jest.SpyInstance;
    const photo1 = new Photo();
    photo1.id = 1;
    photo1.filename = 'me.jpg';

    const user = new User();
    user.firstName = 'Umed Khudoiberdiev';
    user.photos = [photo1];

    beforeEach(() => {
      isCircularSpy = jest.spyOn(TransformOperationExecutor.prototype, 'isCircular' as any);
    });

    afterEach(() => {
      isCircularSpy.mockRestore();
    });

    it('enableCircularCheck option is undefined (default)', () => {
      plainToInstance<User, Record<string, any>>(User, user);
      expect(isCircularSpy).not.toHaveBeenCalled();
    });

    it('enableCircularCheck option is true', () => {
      plainToInstance<User, Record<string, any>>(User, user, { enableCircularCheck: true });
      expect(isCircularSpy).toHaveBeenCalled();
    });
  });
});


================================================
FILE: test/functional/custom-transform.spec.ts
================================================
/* eslint-disable @typescript-eslint/camelcase */
import 'reflect-metadata';
import {
  instanceToInstance,
  instanceToPlain,
  ClassTransformOptions,
  plainToInstance,
  TransformFnParams,
} from '../../src/index';
import { defaultMetadataStorage } from '../../src/storage';
import { Expose, Transform, Type } from '../../src/decorators';
import { TransformationType } from '../../src/enums';

describe('custom transformation decorator', () => {
  it('@Expose decorator with "name" option should work with @Transform decorator', () => {
    defaultMetadataStorage.clear();

    class User {
      @Expose({ name: 'user_name' })
      @Transform(({ value }) => value.toUpperCase())
      name: string;
    }

    const plainUser = {
      user_name: 'Johny Cage',
    };

    const classedUser = plainToInstance(User, plainUser);
    expect(classedUser.name).toEqual('JOHNY CAGE');
  });

  it('@Transform decorator logic should be executed depend of toPlainOnly and toClassOnly set', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;
      name: string;

      @Transform(({ value }) => value.toString(), { toPlainOnly: true })
      @Transform(({ value }) => 'custom-transformed', { toClassOnly: true })
      date: Date;
    }

    const plainUser = {
      id: 1,
      name: 'Johny Cage',
      date: new Date().valueOf(),
    };

    const user = new User();
    user.id = 1;
    user.name = 'Johny Cage';
    user.date = new Date();

    const classedUser = plainToInstance(User, plainUser);
    expect(classedUser).toBeInstanceOf(User);
    expect(classedUser.id).toEqual(1);
    expect(classedUser.name).toEqual('Johny Cage');
    expect(classedUser.date).toBe('custom-transformed');

    const plainedUser = instanceToPlain(user);
    expect(plainedUser).not.toBeInstanceOf(User);
    expect(plainedUser).toEqual({
      id: 1,
      name: 'Johny Cage',
      date: user.date.toString(),
    });
  });

  it('versions and groups should work with @Transform decorator too', () => {
    defaultMetadataStorage.clear();

    class User {
      id: number;
      name: string;

      @Type(() => Date)
      @Transform(({ value }) => 'custom-transformed-version-check', { since: 1, until: 2 })
      date: Date;

      @Type(() => Date)
      @Transform(({ value }) => value.toString(), { groups: ['user'] })
      lastVisitDate: Date;
    }

    const plainUser = {
      id: 1,
      name: 'Johny Cage',
      date: new Date().valueOf(),
      lastVisitDate: new Date().valueOf(),
    };

    const classedUser1 = plainToInstance(User, plainUser);
    expect(classedUser1).toBeInstanceOf(User);
    expect(classedUser1.id).toEqual(1);
    expect(classedUser1.name).toEqual('Johny Cage');
    expect(classedUser1.date).toBe('custom-transformed-version-check');

    const classedUser2 = plainToInstance(User, plainUser, { version: 0.5 });
    expect(classedUser2).toBeInstanceOf(User);
    expect(classedUser2.id).toEqual(1);
    expect(classedUser2.name).toEqual('Johny Cage');
    expect(classedUser2.date).toBeInstanceOf(Date);

    const classedUser3 = plainToInstance(User, plainUser, { version: 1 });
    expect(classedUser3).toBeInstanceOf(User);
    expect(classedUser3.id).toEqual(1);
    expect(classedUser3.name).toEqual('Johny Cage');
    expect(classedUser3.date).toBe('custom-transformed-version-check');

    const classedUser4 = plainToInstance(User, plainUser, { version: 2 });
    expect(classedUser4).toBeInstanceOf(User);
    expect(classedUser4.id).toEqual(1);
    expect(classedUser4.name).toEqual('Johny Cage');
    expect(classedUser4.date).toBeInstanceOf(Date);

    const classedUser5 = plainToInstance(User, plainUser, { groups: ['user'] });
    expect(classedUser5).toBeInstanceOf(User);
    expect(classedUser5.id).toEqual(1);
    expect(classedUser5.name).toEqual('Johny Cage');
    expect(classedUser5.lastVisitDate).toEqual(new Date(plainUser.lastVisitDate).toString());
  });

  it('@Transform decorator callback should be given correct arguments', () => {
    defaultMetadataStorage.clear();

    let keyArg: string;
    let objArg: any;
    let typeArg: TransformationType;
    let optionsArg: ClassTransformOptions;

    function transformCallback({ value, key, obj, type, options }: TransformFnParams): any {
      keyArg = key;
      objArg = obj;
      typeArg = type;
      optionsArg = options;
      return value;
    }

    class User {
      @Transform(transformCallback, { toPlainOnly: true })
      @Transform(transformCallback, { toClassOnly: true })
      name: string;
    }

    const plainUser = {
      name: 'Johny Cage',
    };
    const options: ClassTransformOptions = {
      groups: ['user', 'user.email'],
      version: 2,
    };

    plainToInstance(User, plainUser, options);
    expect(keyArg).toBe('name');
    expect(objArg).toEqual(plainUser);
    expect(typeArg).toEqual(TransformationType.PLAIN_TO_CLASS);
    expect(optionsArg.groups).toBe(options.groups);
    expect(optionsArg.version).toBe(options.version);

    const user = new User();
    user.name = 'Johny Cage';
    optionsArg = undefined;

    instanceToPlain(user, options);
    expect(keyArg).toBe('name');
    expect(objArg).toEqual(user);
    expect(typeArg).toEqual(TransformationType.CLASS_TO_PLAIN);
    expect(optionsArg.groups).toBe(options.groups);
    expect(optionsArg.version).toBe(options.version);
  });

  let model: any;
  it('should serialize json into model instance of class Person', () => {
    defaultMetadataStorage.clear();
    expect(() => {
      const json = {
        name: 'John Doe',
        address: {
          street: 'Main Street 25',
          tel: '5454-534-645',
          zip: 10353,
          country: 'West Samoa',
        },
        age: 25,
        hobbies: [
          { type: 'sport', name: 'sailing' },
          { type: 'relax', name: 'reading' },
          { type: 'sport', name: 'jogging' },
          { type: 'relax', name: 'movies' },
        ],
      };
      class Hobby {
        public type: string;
        public name: string;
      }
      class Address {
        public street: string;

        @Expose({ name: 'tel' })
        public telephone: string;

        public zip: number;

        public country: string;
      }
      class Person {
        public name: string;

        @Type(() => Address)
        public address: Address;

        @Type(() => Hobby)
        @Transform(({ value }) => value.filter((hobby: any) => hobby.type === 'sport'), { toClassOnly: true })
        public hobbies: Hobby[];

        public age: number;
      }
      model = plainToInstance(Person, json);
      expect(model instanceof Person);
      expect(model.address instanceof Address);
      model.hobbies.forEach((hobby: Hobby) => expect(hobby instanceof Hobby && hobby.type === 'sport'));
    }).not.toThrow();
  });

  it('should serialize json into model instance of class Person with different possibilities for type of one property (polymorphism)', () => {
    defaultMetadataStorage.clear();
    expect(() => {
      const json = {
        name: 'John Doe',
        hobby: { __type: 'program', name: 'typescript coding', specialAbility: 'testing' },
      };

      abstract class Hobby {
        public name: string;
      }

      class Sports extends Hobby {
        // Empty
      }

      class Relaxing extends Hobby {
        // Empty
      }

      class Programming extends Hobby {
        @Transform(({ value }) => value.toUpperCase())
        specialAbility: string;
      }

      class Person {
        public name: string;

        @Type(() => Hobby, {
          discriminator: {
            property: '__type',
            subTypes: [
              { value: Sports, name: 'sports' },
              { value: Relaxing, name: 'relax' },
              { value: Programming, name: 'program' },
            ],
          },
        })
        public hobby: any;
      }

      const expectedHobby = { name: 'typescript coding', specialAbility: 'TESTING' };

      const model: Person = plainToInstance(Person, json);
      expect(model).toBeInstanceOf(Person);
      expect(model.hobby).toBeInstanceOf(Programming);
      expect(model.hobby).not.toHaveProperty('__type');
      expect(model.hobby).toHaveProperty('specialAbility', 'TESTING');
    }).not.toThrow();
  });

  it('should serialize json into model instance of class Person with different types in array (polymorphism)', () => {
    defaultMetadataStorage.clear();
    expect(() => {
      const json = {
        name: 'John Doe',
        hobbies: [
          { __type: 'program', name: 'typescript coding', specialAbility: 'testing' },
          { __type: 'relax', name: 'sun' },
        ],
      };

      abstract class Hobby {
        public name: string;
      }

      class Sports extends Hobby {
        // Empty
      }

      class Relaxing extends Hobby {
        // Empty
      }

      class Programming extends Hobby {
        @Transform(({ value }) => value.toUpperCase())
        specialAbility: string;
      }

      class Person {
        public name: string;

        @Type(() => Hobby, {
          discriminator: {
            property: '__type',
            subTypes: [
              { value: Sports, name: 'sports' },
              { value: Relaxing, name: 'relax' },
              { value: Programming, name: 'program' },
            ],
          },
        })
        public hobbies: any[];
      }

      const model: Person = plainToInstance(Person, json);
      expect(model).toBeInstanceOf(Person);
      expect(model.hobbies[0]).toBeInstanceOf(Programming);
      expect(model.hobbies[1]).toBeInstanceOf(Relaxing);
      expect(model.hobbies[0]).not.toHaveProperty('__type');
      expect(model.hobbies[1]).not.toHaveProperty('__type');
      expect(model.hobbies[1]).toHaveProperty('name', 'sun');
      expect(model.hobbies[0]).toHaveProperty('specialAbility', 'TESTING');
    }).not.toThrow();
  });

  it('should serialize json into model instance of class Person with different possibilities for type of one property AND keeps discriminator property (polymorphism)', () => {
    defaultMetadataStorage.clear();
    expect(() => {
      const json = {
        name: 
Download .txt
gitextract_zd9snzjs/

├── .eslintrc.yml
├── .gitbook.yaml
├── .github/
│   ├── dependabot.yml
│   ├── semantic.yml
│   └── workflows/
│       ├── auto-approve-dependabot-workflow.yml
│       ├── continuous-deployment-workflow.yml
│       ├── continuous-integration-workflow.yml
│       └── lock-closed-issues-workflow.yml
├── .gitignore
├── .prettierrc.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── codecov.yml
├── docs/
│   ├── SUMMARY.md
│   └── pages/
│       ├── 01-getting-started.md
│       └── 02-basic-usage.md
├── jest.config.js
├── package.json
├── rollup.config.js
├── sample/
│   ├── sample1-simple-usage/
│   │   ├── Album.ts
│   │   ├── Photo.ts
│   │   ├── User.ts
│   │   └── app.ts
│   ├── sample2-iheritance/
│   │   ├── Album.ts
│   │   ├── Authorable.ts
│   │   ├── Photo.ts
│   │   ├── User.ts
│   │   └── app.ts
│   ├── sample3-custom-arrays/
│   │   ├── Album.ts
│   │   ├── AlbumArray.ts
│   │   ├── Photo.ts
│   │   └── app.ts
│   ├── sample4-generics/
│   │   ├── SimpleCollection.ts
│   │   ├── SuperCollection.ts
│   │   ├── User.ts
│   │   └── app.ts
│   └── sample5-custom-transformer/
│       ├── User.ts
│       └── app.ts
├── src/
│   ├── ClassTransformer.ts
│   ├── MetadataStorage.ts
│   ├── TransformOperationExecutor.ts
│   ├── constants/
│   │   └── default-options.constant.ts
│   ├── decorators/
│   │   ├── exclude.decorator.ts
│   │   ├── expose.decorator.ts
│   │   ├── index.ts
│   │   ├── transform-instance-to-instance.decorator.ts
│   │   ├── transform-instance-to-plain.decorator.ts
│   │   ├── transform-plain-to-instance.decorator.ts
│   │   ├── transform.decorator.ts
│   │   └── type.decorator.ts
│   ├── enums/
│   │   ├── index.ts
│   │   └── transformation-type.enum.ts
│   ├── index.ts
│   ├── interfaces/
│   │   ├── class-constructor.type.ts
│   │   ├── class-transformer-options.interface.ts
│   │   ├── decorator-options/
│   │   │   ├── exclude-options.interface.ts
│   │   │   ├── expose-options.interface.ts
│   │   │   ├── transform-options.interface.ts
│   │   │   ├── type-discriminator-descriptor.interface.ts
│   │   │   └── type-options.interface.ts
│   │   ├── index.ts
│   │   ├── metadata/
│   │   │   ├── exclude-metadata.interface.ts
│   │   │   ├── expose-metadata.interface.ts
│   │   │   ├── transform-fn-params.interface.ts
│   │   │   ├── transform-metadata.interface.ts
│   │   │   └── type-metadata.interface.ts
│   │   ├── target-map.interface.ts
│   │   └── type-help-options.interface.ts
│   ├── storage.ts
│   └── utils/
│       ├── get-global.util.spect.ts
│       ├── get-global.util.ts
│       ├── index.ts
│       └── is-promise.util.ts
├── test/
│   └── functional/
│       ├── basic-functionality.spec.ts
│       ├── circular-reference-problem.spec.ts
│       ├── custom-transform.spec.ts
│       ├── default-values.spec.ts
│       ├── es6-data-types.spec.ts
│       ├── ignore-decorators.spec.ts
│       ├── implicit-type-declarations.spec.ts
│       ├── inheritence.spec.ts
│       ├── prevent-array-bomb.spec.ts
│       ├── promise-field.spec.ts
│       ├── serialization-deserialization.spec.ts
│       ├── specify-maps.spec.ts
│       ├── transformation-option.spec.ts
│       ├── transformer-method.spec.ts
│       └── transformer-order.spec.ts
├── tsconfig.json
├── tsconfig.prod.cjs.json
├── tsconfig.prod.esm2015.json
├── tsconfig.prod.esm5.json
├── tsconfig.prod.json
├── tsconfig.prod.types.json
└── tsconfig.spec.json
Download .txt
SYMBOL INDEX (276 symbols across 57 files)

FILE: sample/sample1-simple-usage/Album.ts
  class Album (line 4) | class Album {

FILE: sample/sample1-simple-usage/Photo.ts
  class Photo (line 5) | class Photo {
    method name (line 20) | get name() {
    method getAlbums (line 24) | getAlbums() {

FILE: sample/sample1-simple-usage/User.ts
  class User (line 3) | class User {

FILE: sample/sample2-iheritance/Album.ts
  class Album (line 5) | class Album extends Authorable {

FILE: sample/sample2-iheritance/Authorable.ts
  class Authorable (line 4) | class Authorable {

FILE: sample/sample2-iheritance/Photo.ts
  class Photo (line 5) | class Photo extends Authorable {

FILE: sample/sample2-iheritance/User.ts
  class User (line 3) | class User {

FILE: sample/sample3-custom-arrays/Album.ts
  class Album (line 1) | class Album {

FILE: sample/sample3-custom-arrays/AlbumArray.ts
  class AlbumArray (line 3) | class AlbumArray extends Array<Album> {
    method findByName (line 4) | findByName(name: string) {

FILE: sample/sample3-custom-arrays/Photo.ts
  class Photo (line 5) | class Photo {

FILE: sample/sample4-generics/SimpleCollection.ts
  class SimpleCollection (line 1) | class SimpleCollection<T> {

FILE: sample/sample4-generics/SuperCollection.ts
  class SuperCollection (line 3) | class SuperCollection<T> {
    method constructor (line 14) | constructor(type: Function) {

FILE: sample/sample4-generics/User.ts
  class User (line 3) | class User {
    method constructor (line 13) | constructor(id: number, firstName: string, lastName: string, password:...
    method name (line 20) | get name() {

FILE: sample/sample5-custom-transformer/User.ts
  class User (line 4) | class User {

FILE: src/ClassTransformer.ts
  class ClassTransformer (line 7) | class ClassTransformer {
    method instanceToPlain (line 17) | instanceToPlain<T extends Record<string, any>>(
    method classToPlainFromExist (line 43) | classToPlainFromExist<T extends Record<string, any>, P>(
    method plainToInstance (line 68) | plainToInstance<T extends Record<string, any>, V>(
    method plainToClassFromExist (line 91) | plainToClassFromExist<T extends Record<string, any>, V>(
    method instanceToInstance (line 108) | instanceToInstance<T>(object: T | T[], options?: ClassTransformOptions...
    method classToClassFromExist (line 123) | classToClassFromExist<T>(object: T, fromObject: T | T[], options?: Cla...
    method serialize (line 136) | serialize<T>(object: T | T[], options?: ClassTransformOptions): string {
    method deserialize (line 143) | deserialize<T>(cls: ClassConstructor<T>, json: string, options?: Class...
    method deserializeArray (line 151) | deserializeArray<T>(cls: ClassConstructor<T>, json: string, options?: ...

FILE: src/MetadataStorage.ts
  class MetadataStorage (line 7) | class MetadataStorage {
    method addTypeMetadata (line 22) | addTypeMetadata(metadata: TypeMetadata): void {
    method addTransformMetadata (line 29) | addTransformMetadata(metadata: TransformMetadata): void {
    method addExposeMetadata (line 39) | addExposeMetadata(metadata: ExposeMetadata): void {
    method addExcludeMetadata (line 46) | addExcludeMetadata(metadata: ExcludeMetadata): void {
    method findTransformMetadatas (line 57) | findTransformMetadatas(
    method findExcludeMetadata (line 80) | findExcludeMetadata(target: Function, propertyName: string): ExcludeMe...
    method findExposeMetadata (line 84) | findExposeMetadata(target: Function, propertyName: string): ExposeMeta...
    method findExposeMetadataByCustomName (line 88) | findExposeMetadataByCustomName(target: Function, name: string): Expose...
    method findTypeMetadata (line 94) | findTypeMetadata(target: Function, propertyName: string): TypeMetadata {
    method getStrategy (line 98) | getStrategy(target: Function): 'excludeAll' | 'exposeAll' | 'none' {
    method getExposedMetadatas (line 107) | getExposedMetadatas(target: Function): ExposeMetadata[] {
    method getExcludedMetadatas (line 111) | getExcludedMetadatas(target: Function): ExcludeMetadata[] {
    method getExposedProperties (line 115) | getExposedProperties(target: Function, transformationType: Transformat...
    method getExcludedProperties (line 136) | getExcludedProperties(target: Function, transformationType: Transforma...
    method clear (line 157) | clear(): void {
    method getMetadata (line 168) | private getMetadata<T extends { target: Function; propertyName: string...
    method findMetadata (line 190) | private findMetadata<T extends { target: Function; propertyName: strin...
    method findMetadatas (line 214) | private findMetadatas<T extends { target: Function; propertyName: stri...
    method getAncestors (line 239) | private getAncestors(target: Function): Function[] {

FILE: src/TransformOperationExecutor.ts
  function instantiateArrayType (line 6) | function instantiateArrayType(arrayType: Function): Array<any> | Set<any> {
  class TransformOperationExecutor (line 14) | class TransformOperationExecutor {
    method constructor (line 25) | constructor(private transformationType: TransformationType, private op...
    method transform (line 31) | transform(
    method applyCustomTransformations (line 380) | private applyCustomTransformations(
    method isCircular (line 419) | private isCircular(object: Record<string, any>): boolean {
    method getReflectedType (line 423) | private getReflectedType(target: Function, propertyName: string): Func...
    method getKeys (line 429) | private getKeys(target: Function, object: Record<string, any>, isMap: ...
    method checkVersion (line 534) | private checkVersion(since: number, until: number): boolean {
    method checkGroups (line 542) | private checkGroups(groups: string[]): boolean {
    method getPropertyDescriptor (line 548) | private getPropertyDescriptor(obj: any, key: PropertyKey): PropertyDes...

FILE: src/decorators/exclude.decorator.ts
  function Exclude (line 11) | function Exclude(options: ExcludeOptions = {}): PropertyDecorator & Clas...

FILE: src/decorators/expose.decorator.ts
  function Expose (line 11) | function Expose(options: ExposeOptions = {}): PropertyDecorator & ClassD...

FILE: src/decorators/transform-instance-to-instance.decorator.ts
  function TransformInstanceToInstance (line 9) | function TransformInstanceToInstance(params?: ClassTransformOptions): Me...

FILE: src/decorators/transform-instance-to-plain.decorator.ts
  function TransformInstanceToPlain (line 9) | function TransformInstanceToPlain(params?: ClassTransformOptions): Metho...

FILE: src/decorators/transform-plain-to-instance.decorator.ts
  function TransformPlainToInstance (line 9) | function TransformPlainToInstance(

FILE: src/decorators/transform.decorator.ts
  function Transform (line 9) | function Transform(

FILE: src/decorators/type.decorator.ts
  function Type (line 10) | function Type(

FILE: src/enums/transformation-type.enum.ts
  type TransformationType (line 1) | enum TransformationType {

FILE: src/index.ts
  function classToPlain (line 19) | function classToPlain<T>(
  function instanceToPlain (line 31) | function instanceToPlain<T>(
  function classToPlainFromExist (line 55) | function classToPlainFromExist<T>(
  function plainToClass (line 70) | function plainToClass<T, V>(cls: ClassConstructor<T>, plain: V | V[], op...
  function plainToInstance (line 79) | function plainToInstance<T, V>(
  function plainToClassFromExist (line 96) | function plainToClassFromExist<T, V>(clsObject: T, plain: V | V[], optio...
  function instanceToInstance (line 105) | function instanceToInstance<T>(object: T | T[], options?: ClassTransform...
  function classToClassFromExist (line 118) | function classToClassFromExist<T>(object: T, fromObject: T | T[], option...
  function serialize (line 132) | function serialize<T>(object: T | T[], options?: ClassTransformOptions):...
  function deserialize (line 144) | function deserialize<T>(cls: ClassConstructor<T>, json: string, options?...
  function deserializeArray (line 157) | function deserializeArray<T>(cls: ClassConstructor<T>, json: string, opt...

FILE: src/interfaces/class-constructor.type.ts
  type ClassConstructor (line 1) | type ClassConstructor<T> = {

FILE: src/interfaces/class-transformer-options.interface.ts
  type ClassTransformOptions (line 6) | interface ClassTransformOptions {

FILE: src/interfaces/decorator-options/exclude-options.interface.ts
  type ExcludeOptions (line 4) | interface ExcludeOptions {

FILE: src/interfaces/decorator-options/expose-options.interface.ts
  type ExposeOptions (line 4) | interface ExposeOptions {

FILE: src/interfaces/decorator-options/transform-options.interface.ts
  type TransformOptions (line 4) | interface TransformOptions {

FILE: src/interfaces/decorator-options/type-discriminator-descriptor.interface.ts
  type DiscriminatorDescriptor (line 7) | interface DiscriminatorDescriptor {

FILE: src/interfaces/decorator-options/type-options.interface.ts
  type TypeOptions (line 6) | interface TypeOptions {

FILE: src/interfaces/metadata/exclude-metadata.interface.ts
  type ExcludeMetadata (line 6) | interface ExcludeMetadata {

FILE: src/interfaces/metadata/expose-metadata.interface.ts
  type ExposeMetadata (line 6) | interface ExposeMetadata {

FILE: src/interfaces/metadata/transform-fn-params.interface.ts
  type TransformFnParams (line 4) | interface TransformFnParams {

FILE: src/interfaces/metadata/transform-metadata.interface.ts
  type TransformMetadata (line 7) | interface TransformMetadata {

FILE: src/interfaces/metadata/type-metadata.interface.ts
  type TypeMetadata (line 6) | interface TypeMetadata {

FILE: src/interfaces/target-map.interface.ts
  type TargetMap (line 5) | interface TargetMap {

FILE: src/interfaces/type-help-options.interface.ts
  type TypeHelpOptions (line 2) | interface TypeHelpOptions {

FILE: src/utils/get-global.util.ts
  function getGlobal (line 7) | function getGlobal() {

FILE: src/utils/is-promise.util.ts
  function isPromise (line 1) | function isPromise<T>(p: any): p is Promise<T> {

FILE: test/functional/basic-functionality.spec.ts
  class User (line 17) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 99) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class ExampleClass (line 125) | class ExampleClass {
  class User (line 158) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 237) | @Exclude()
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 302) | @Exclude()
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 385) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 467) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 543) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Photo (line 732) | class Photo {
  class User (line 742) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Photo (line 807) | class Photo {
  class ExtendedPhoto (line 815) | class ExtendedPhoto implements Photo {
  class User (line 824) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Photo (line 864) | class Photo {
  class User (line 875) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Photo (line 980) | class Photo {
  class User (line 996) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Photo (line 1255) | class Photo {
  class User (line 1270) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 1502) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 1551) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Photo (line 1603) | class Photo {
  class User (line 1609) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class User (line 1668) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class Programmer (line 1682) | class Programmer extends User {
  class User (line 1719) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class TestClass (line 1852) | class TestClass {
  class User (line 1878) | class User {
    method name (line 1510) | get name(): string {
    method getName (line 1515) | getName(): string {
    method name (line 1562) | get name(): string {
    method getName (line 1567) | getName(): string {
    method name (line 1621) | get name(): string {
    method name (line 1673) | get name() {
    method getName (line 1678) | getName() {
    method name (line 1728) | get name(): string {
  class TestClass (line 1882) | class TestClass {

FILE: test/functional/circular-reference-problem.spec.ts
  class Caption (line 10) | class Caption {
  class Photo (line 14) | class Photo {
  class User (line 22) | class User {
  class Photo (line 77) | class Photo {
  class User (line 84) | class User {
  class Photo (line 114) | class Photo {
  class User (line 119) | class User {

FILE: test/functional/custom-transform.spec.ts
  class User (line 18) | class User {
  class User (line 35) | class User {
  class User (line 73) | class User {
  function transformCallback (line 132) | function transformCallback({ value, key, obj, type, options }: Transform...
  class User (line 140) | class User {
  class Hobby (line 193) | class Hobby {
  class Address (line 197) | class Address {
  class Person (line 207) | class Person {
  class Sports (line 238) | class Sports extends Hobby {
  class Relaxing (line 242) | class Relaxing extends Hobby {
  class Programming (line 246) | class Programming extends Hobby {
  class Person (line 251) | class Person {
  class Sports (line 292) | class Sports extends Hobby {
  class Relaxing (line 296) | class Relaxing extends Hobby {
  class Programming (line 300) | class Programming extends Hobby {
  class Person (line 305) | class Person {
  class Sports (line 344) | class Sports extends Hobby {
  class Relaxing (line 348) | class Relaxing extends Hobby {
  class Programming (line 352) | class Programming extends Hobby {
  class Person (line 357) | class Person {
  class Sports (line 397) | class Sports extends Hobby {
  class Relaxing (line 401) | class Relaxing extends Hobby {
  class Programming (line 405) | class Programming extends Hobby {
  class Person (line 410) | class Person {
  class Sports (line 445) | class Sports extends Hobby {
  class Relaxing (line 449) | class Relaxing extends Hobby {
  class Programming (line 453) | class Programming extends Hobby {
  class Person (line 458) | class Person {
  class Sports (line 493) | class Sports extends Hobby {
  class Relaxing (line 497) | class Relaxing extends Hobby {
  class Programming (line 501) | class Programming extends Hobby {
  class Person (line 506) | class Person {
  class Sports (line 547) | class Sports extends Hobby {
  class Relaxing (line 551) | class Relaxing extends Hobby {
  class Programming (line 555) | class Programming extends Hobby {
  class Person (line 560) | class Person {
  class Sports (line 598) | class Sports extends Hobby {
  class Relaxing (line 602) | class Relaxing extends Hobby {
  class Programming (line 606) | class Programming extends Hobby {
  class Person (line 611) | class Person {
  class Sports (line 646) | class Sports extends Hobby {
  class Relaxing (line 650) | class Relaxing extends Hobby {
  class Programming (line 654) | class Programming extends Hobby {
  class Person (line 659) | class Person {
  class Sports (line 702) | class Sports extends Hobby {
  class Relaxing (line 706) | class Relaxing extends Hobby {
  class Programming (line 710) | class Programming extends Hobby {
  class Person (line 715) | class Person {
  class Sports (line 750) | class Sports extends Hobby {
  class Relaxing (line 754) | class Relaxing extends Hobby {
  class Programming (line 758) | class Programming extends Hobby {
  class Person (line 763) | class Person {

FILE: test/functional/default-values.spec.ts
  class User (line 4) | class User {

FILE: test/functional/es6-data-types.spec.ts
  class User (line 10) | class User {
  class User (line 63) | class User {
  class Weapon (line 108) | class Weapon {
    method constructor (line 109) | constructor(public model: string, public range: number) {}
    method constructor (line 196) | constructor(public model: string, public range: number) {}
    method constructor (line 260) | constructor(public model: string, public range: number) {}
  class User (line 112) | class User {
  class Weapon (line 195) | class Weapon {
    method constructor (line 109) | constructor(public model: string, public range: number) {}
    method constructor (line 196) | constructor(public model: string, public range: number) {}
    method constructor (line 260) | constructor(public model: string, public range: number) {}
  class User (line 199) | class User {
  class Weapon (line 259) | class Weapon {
    method constructor (line 109) | constructor(public model: string, public range: number) {}
    method constructor (line 196) | constructor(public model: string, public range: number) {}
    method constructor (line 260) | constructor(public model: string, public range: number) {}
  class User (line 263) | class User {
  function checkPlainToClassUser (line 320) | function checkPlainToClassUser(classUser: User) {

FILE: test/functional/ignore-decorators.spec.ts
  class User (line 10) | class User {

FILE: test/functional/implicit-type-declarations.spec.ts
  class SimpleExample (line 10) | class SimpleExample {
  class Example (line 44) | class Example {
  class Example (line 84) | class Example {

FILE: test/functional/inheritence.spec.ts
  class Contact (line 9) | class Contact {
  class User (line 16) | class User extends Contact {
  class Student (line 22) | class Student extends User {

FILE: test/functional/prevent-array-bomb.spec.ts
  class TestClass (line 9) | class TestClass {

FILE: test/functional/promise-field.spec.ts
  class PromiseClass (line 9) | class PromiseClass {
    method constructor (line 27) | constructor(promise: Promise<any>) {
  class PromiseClass (line 24) | class PromiseClass {
    method constructor (line 27) | constructor(promise: Promise<any>) {
  class PromiseClass (line 42) | class PromiseClass {
    method constructor (line 27) | constructor(promise: Promise<any>) {
  class PromiseClass (line 61) | class PromiseClass {
    method constructor (line 27) | constructor(promise: Promise<any>) {
  class InnerClass (line 66) | class InnerClass {
    method constructor (line 69) | constructor(position: string) {

FILE: test/functional/serialization-deserialization.spec.ts
  class User (line 10) | class User {
  class TestObject (line 99) | class TestObject {
    method getterOnlyProp (line 119) | get getterOnlyProp(): string {
  class TestObject (line 118) | class TestObject {
    method getterOnlyProp (line 119) | get getterOnlyProp(): string {

FILE: test/functional/specify-maps.spec.ts
  class User (line 17) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 99) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 178) | @Exclude()
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 243) | @Exclude()
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 326) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 408) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 484) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class Photo (line 606) | class Photo {
  class User (line 616) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class Photo (line 681) | class Photo {
  class ExtendedPhoto (line 689) | class ExtendedPhoto implements Photo {
  class User (line 698) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class Photo (line 738) | class Photo {
  class User (line 749) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class Photo (line 854) | class Photo {
  class User (line 870) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class Photo (line 1129) | class Photo {
  class User (line 1144) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 1376) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 1425) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class Photo (line 1477) | class Photo {
  class User (line 1483) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {
  class User (line 1544) | class User {
    method name (line 1384) | get name(): string {
    method getName (line 1389) | getName(): string {
    method name (line 1436) | get name(): string {
    method getName (line 1441) | getName(): string {
    method name (line 1495) | get name(): string {
    method name (line 1553) | get name(): string {

FILE: test/functional/transformation-option.spec.ts
  class User (line 10) | class User {
  class User (line 48) | class User {
  class User (line 86) | @Exclude()
  class User (line 127) | @Exclude()
  class User (line 168) | @Exclude()

FILE: test/functional/transformer-method.spec.ts
  class User (line 15) | @Exclude()
  class UserController (line 28) | class UserController {
    method getUser (line 30) | getUser(): User {
    method getUser (line 72) | getUser(): User {
    method getUser (line 113) | getUser(): User {
    method getUserWithRoles (line 157) | getUserWithRoles(): User {
    method getUserVersion1 (line 206) | getUserVersion1(): User {
    method getUserVersion2 (line 218) | getUserVersion2(): User {
  class User (line 57) | @Exclude()
  class UserController (line 70) | class UserController {
    method getUser (line 30) | getUser(): User {
    method getUser (line 72) | getUser(): User {
    method getUser (line 113) | getUser(): User {
    method getUserWithRoles (line 157) | getUserWithRoles(): User {
    method getUserVersion1 (line 206) | getUserVersion1(): User {
    method getUserVersion2 (line 218) | getUserVersion2(): User {
  class User (line 98) | @Exclude()
  class UserController (line 111) | class UserController {
    method getUser (line 30) | getUser(): User {
    method getUser (line 72) | getUser(): User {
    method getUser (line 113) | getUser(): User {
    method getUserWithRoles (line 157) | getUserWithRoles(): User {
    method getUserVersion1 (line 206) | getUserVersion1(): User {
    method getUserVersion2 (line 218) | getUserVersion2(): User {
  class User (line 139) | @Exclude()
  class UserController (line 155) | class UserController {
    method getUser (line 30) | getUser(): User {
    method getUser (line 72) | getUser(): User {
    method getUser (line 113) | getUser(): User {
    method getUserWithRoles (line 157) | getUserWithRoles(): User {
    method getUserVersion1 (line 206) | getUserVersion1(): User {
    method getUserVersion2 (line 218) | getUserVersion2(): User {
  class User (line 185) | @Exclude()
  class UserController (line 204) | class UserController {
    method getUser (line 30) | getUser(): User {
    method getUser (line 72) | getUser(): User {
    method getUser (line 113) | getUser(): User {
    method getUserWithRoles (line 157) | getUserWithRoles(): User {
    method getUserVersion1 (line 206) | getUserVersion1(): User {
    method getUserVersion2 (line 218) | getUserVersion2(): User {

FILE: test/functional/transformer-order.spec.ts
  class User (line 11) | class User {
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (302K chars).
[
  {
    "path": ".eslintrc.yml",
    "chars": 1214,
    "preview": "parser: '@typescript-eslint/parser'\nplugins:\n  - '@typescript-eslint'\nparserOptions:\n  ecmaVersion: 2018\n  sourceType: m"
  },
  {
    "path": ".gitbook.yaml",
    "chars": 89,
    "preview": "root: ./docs\n​structure:  \n  readme: pages/01-getting-started.md  \n  summary: SUMMARY.md​"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 300,
    "preview": "version: 2\nupdates:\n- package-ecosystem: npm\n  directory: \"/\"\n  schedule:\n    interval: daily\n    time: \"10:00\"\n    time"
  },
  {
    "path": ".github/semantic.yml",
    "chars": 199,
    "preview": "titleAndCommits: true\nallowMergeCommits: false\nscopes:\n  - deps\n  - deps-dev\ntypes:\n  - feat\n  - fix\n  - docs\n  - style\n"
  },
  {
    "path": ".github/workflows/auto-approve-dependabot-workflow.yml",
    "chars": 704,
    "preview": "name: Dependabot auto-merge\non:\n  pull_request_target\njobs:\n  dependabot:\n    runs-on: ubuntu-latest\n    if: github.acto"
  },
  {
    "path": ".github/workflows/continuous-deployment-workflow.yml",
    "chars": 831,
    "preview": "name: CD\non:\n  release:\n    types: [created]\njobs:\n  publish:\n    name: Publish to NPM\n    runs-on: ubuntu-latest\n    st"
  },
  {
    "path": ".github/workflows/continuous-integration-workflow.yml",
    "chars": 1299,
    "preview": "name: CI\non: [push, pull_request]\njobs:\n  checks:\n    name: Linters\n    runs-on: ubuntu-latest\n    steps:\n      - uses: "
  },
  {
    "path": ".github/workflows/lock-closed-issues-workflow.yml",
    "chars": 760,
    "preview": "name: 'Lock inactive threads'\non:\n  schedule:\n    - cron: '0 0 * * *'\njobs:\n  lock:\n    name: Lock closed issues\n    run"
  },
  {
    "path": ".gitignore",
    "chars": 646,
    "preview": "# Log files\r\nlogs\r\n*.log\r\n*.tmp\r\n*.tmp.*\r\nlog.txt\r\nnpm-debug.log*\r\n\r\n# Testing output\r\nlib-cov/**\r\ncoverage/**\r\n\r\n# Envi"
  },
  {
    "path": ".prettierrc.yml",
    "chars": 131,
    "preview": "printWidth: 120\ntabWidth: 2\nuseTabs: false\nsemi: true\nsingleQuote: true\ntrailingComma: es5\nbracketSpacing: true\narrowPar"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 7231,
    "preview": "# Changelog\n\n_This changelog follows the [keep a changelog][keep-a-changelog]_ format to maintain a human readable chang"
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "The MIT License\n\nCopyright (c) 2015-2020 TypeStack\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "README.md",
    "chars": 26699,
    "preview": "# class-transformer\n\n![Build Status](https://github.com/typestack/class-transformer/workflows/CI/badge.svg)\n[![codecov]("
  },
  {
    "path": "codecov.yml",
    "chars": 223,
    "preview": "coverage:\n  range: 70..100\n  round: down\n  precision: 2\n  status:\n    project:\n      default:\n        threshold: 0%\n    "
  },
  {
    "path": "docs/SUMMARY.md",
    "chars": 111,
    "preview": "# Table of contents\n\n- [Getting Started](pages/01-getting-started.md)\n- [Basic usage](pages/02-basis-usage.md)\n"
  },
  {
    "path": "docs/pages/01-getting-started.md",
    "chars": 2072,
    "preview": "# Getting Started\n\nThe `class-transformer` package is a zero-dependency utility library helping you to quickly transform"
  },
  {
    "path": "docs/pages/02-basic-usage.md",
    "chars": 1189,
    "preview": "# Basic usage\n\nThere are two main exported functions what can be used for transformations:\n\n- `plainToInstance` - transf"
  },
  {
    "path": "jest.config.js",
    "chars": 244,
    "preview": "module.exports = {\r\n  preset: 'ts-jest',\r\n  testEnvironment: 'node',\r\n  collectCoverageFrom: ['src/**/*.ts', '!src/**/in"
  },
  {
    "path": "package.json",
    "chars": 2329,
    "preview": "{\n  \"name\": \"class-transformer\",\n  \"version\": \"0.5.1\",\n  \"description\": \"Proper decorator-based transformation / seriali"
  },
  {
    "path": "rollup.config.js",
    "chars": 580,
    "preview": "import { nodeResolve } from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport { ters"
  },
  {
    "path": "sample/sample1-simple-usage/Album.ts",
    "chars": 196,
    "preview": "import { Type, Exclude } from '../../src/decorators';\nimport { Photo } from './Photo';\n\nexport class Album {\n  id: strin"
  },
  {
    "path": "sample/sample1-simple-usage/Photo.ts",
    "chars": 452,
    "preview": "import { Type } from '../../src/decorators';\nimport { Album } from './Album';\nimport { User } from './User';\n\nexport cla"
  },
  {
    "path": "sample/sample1-simple-usage/User.ts",
    "chars": 194,
    "preview": "import { Type } from '../../src/decorators';\n\nexport class User {\n  @Type(() => Number)\n  id: number;\n\n  firstName: stri"
  },
  {
    "path": "sample/sample1-simple-usage/app.ts",
    "chars": 1701,
    "preview": "import 'es6-shim';\nimport 'reflect-metadata';\nimport { plainToClass, classToPlain } from '../../src/index';\nimport { Pho"
  },
  {
    "path": "sample/sample2-iheritance/Album.ts",
    "chars": 258,
    "preview": "import { Type, Exclude } from '../../src/decorators';\nimport { Photo } from './Photo';\nimport { Authorable } from './Aut"
  },
  {
    "path": "sample/sample2-iheritance/Authorable.ts",
    "chars": 210,
    "preview": "import { Type, Exclude } from '../../src/decorators';\nimport { User } from './User';\n\nexport class Authorable {\n  author"
  },
  {
    "path": "sample/sample2-iheritance/Photo.ts",
    "chars": 371,
    "preview": "import { Type, Exclude } from '../../src/decorators';\nimport { Album } from './Album';\nimport { Authorable } from './Aut"
  },
  {
    "path": "sample/sample2-iheritance/User.ts",
    "chars": 194,
    "preview": "import { Type } from '../../src/decorators';\n\nexport class User {\n  @Type(() => Number)\n  id: number;\n\n  firstName: stri"
  },
  {
    "path": "sample/sample2-iheritance/app.ts",
    "chars": 1851,
    "preview": "import 'es6-shim';\nimport 'reflect-metadata';\nimport { classToPlain, plainToClass } from '../../src/index';\nimport { Pho"
  },
  {
    "path": "sample/sample3-custom-arrays/Album.ts",
    "chars": 54,
    "preview": "export class Album {\n  id: string;\n\n  name: string;\n}\n"
  },
  {
    "path": "sample/sample3-custom-arrays/AlbumArray.ts",
    "chars": 168,
    "preview": "import { Album } from './Album';\n\nexport class AlbumArray extends Array<Album> {\n  findByName(name: string) {\n    return"
  },
  {
    "path": "sample/sample3-custom-arrays/Photo.ts",
    "chars": 267,
    "preview": "import { Album } from './Album';\nimport { AlbumArray } from './AlbumArray';\nimport { Type } from '../../src/decorators';"
  },
  {
    "path": "sample/sample3-custom-arrays/app.ts",
    "chars": 831,
    "preview": "import 'es6-shim';\nimport 'reflect-metadata';\nimport { classToPlain, plainToClass } from '../../src/index';\nimport { Pho"
  },
  {
    "path": "sample/sample4-generics/SimpleCollection.ts",
    "chars": 68,
    "preview": "export class SimpleCollection<T> {\n  items: T[];\n  count: number;\n}\n"
  },
  {
    "path": "sample/sample4-generics/SuperCollection.ts",
    "chars": 307,
    "preview": "import { Type, Exclude } from '../../src/decorators';\n\nexport class SuperCollection<T> {\n  @Exclude()\n  private type: Fu"
  },
  {
    "path": "sample/sample4-generics/User.ts",
    "chars": 429,
    "preview": "import { Exclude } from '../../src/decorators';\n\nexport class User {\n  id: number;\n\n  firstName: string;\n\n  lastName: st"
  },
  {
    "path": "sample/sample4-generics/app.ts",
    "chars": 1031,
    "preview": "import 'es6-shim';\nimport 'reflect-metadata';\nimport { SimpleCollection } from './SimpleCollection';\nimport { User } fro"
  },
  {
    "path": "sample/sample5-custom-transformer/User.ts",
    "chars": 302,
    "preview": "import { Type, Transform } from '../../src/decorators';\nimport * as moment from 'moment';\n\nexport class User {\n  id: num"
  },
  {
    "path": "sample/sample5-custom-transformer/app.ts",
    "chars": 388,
    "preview": "import 'es6-shim';\nimport 'reflect-metadata';\nimport { plainToClass, classToPlain } from '../../src/index';\nimport { Use"
  },
  {
    "path": "src/ClassTransformer.ts",
    "chars": 6061,
    "preview": "import { ClassTransformOptions } from './interfaces';\nimport { TransformOperationExecutor } from './TransformOperationEx"
  },
  {
    "path": "src/MetadataStorage.ts",
    "chars": 9590,
    "preview": "import { TypeMetadata, ExposeMetadata, ExcludeMetadata, TransformMetadata } from './interfaces';\nimport { Transformation"
  },
  {
    "path": "src/TransformOperationExecutor.ts",
    "chars": 22662,
    "preview": "import { defaultMetadataStorage } from './storage';\nimport { ClassTransformOptions, TypeHelpOptions, TypeMetadata, TypeO"
  },
  {
    "path": "src/constants/default-options.constant.ts",
    "chars": 542,
    "preview": "import { ClassTransformOptions } from '../interfaces/class-transformer-options.interface';\n\n/**\n * These are the default"
  },
  {
    "path": "src/decorators/exclude.decorator.ts",
    "chars": 1057,
    "preview": "import { defaultMetadataStorage } from '../storage';\nimport { ExcludeOptions } from '../interfaces';\n\n/**\n * Marks the g"
  },
  {
    "path": "src/decorators/expose.decorator.ts",
    "chars": 1053,
    "preview": "import { defaultMetadataStorage } from '../storage';\nimport { ExposeOptions } from '../interfaces';\n\n/**\n * Marks the gi"
  },
  {
    "path": "src/decorators/index.ts",
    "chars": 320,
    "preview": "export * from './exclude.decorator';\nexport * from './expose.decorator';\nexport * from './transform-instance-to-instance"
  },
  {
    "path": "src/decorators/transform-instance-to-instance.decorator.ts",
    "chars": 1036,
    "preview": "import { ClassTransformer } from '../ClassTransformer';\nimport { ClassTransformOptions } from '../interfaces';\n\n/**\n * R"
  },
  {
    "path": "src/decorators/transform-instance-to-plain.decorator.ts",
    "chars": 1060,
    "preview": "import { ClassTransformer } from '../ClassTransformer';\nimport { ClassTransformOptions } from '../interfaces';\n\n/**\n * T"
  },
  {
    "path": "src/decorators/transform-plain-to-instance.decorator.ts",
    "chars": 1107,
    "preview": "import { ClassTransformer } from '../ClassTransformer';\nimport { ClassTransformOptions, ClassConstructor } from '../inte"
  },
  {
    "path": "src/decorators/transform.decorator.ts",
    "chars": 606,
    "preview": "import { defaultMetadataStorage } from '../storage';\nimport { TransformFnParams, TransformOptions } from '../interfaces'"
  },
  {
    "path": "src/decorators/type.decorator.ts",
    "chars": 785,
    "preview": "import { defaultMetadataStorage } from '../storage';\nimport { TypeHelpOptions, TypeOptions } from '../interfaces';\n\n/**\n"
  },
  {
    "path": "src/enums/index.ts",
    "chars": 44,
    "preview": "export * from './transformation-type.enum';\n"
  },
  {
    "path": "src/enums/transformation-type.enum.ts",
    "chars": 89,
    "preview": "export enum TransformationType {\n  PLAIN_TO_CLASS,\n  CLASS_TO_PLAIN,\n  CLASS_TO_CLASS,\n}\n"
  },
  {
    "path": "src/index.ts",
    "chars": 6656,
    "preview": "import { ClassTransformer } from './ClassTransformer';\nimport { ClassTransformOptions } from './interfaces';\nimport { Cl"
  },
  {
    "path": "src/interfaces/class-constructor.type.ts",
    "chars": 66,
    "preview": "export type ClassConstructor<T> = {\n  new (...args: any[]): T;\n};\n"
  },
  {
    "path": "src/interfaces/class-transformer-options.interface.ts",
    "chars": 2770,
    "preview": "import { TargetMap } from './target-map.interface';\n\n/**\n * Options to be passed during transformation.\n */\nexport inter"
  },
  {
    "path": "src/interfaces/decorator-options/exclude-options.interface.ts",
    "chars": 349,
    "preview": "/**\n * Possible transformation options for the @Exclude decorator.\n */\nexport interface ExcludeOptions {\n  /**\n   * Excl"
  },
  {
    "path": "src/interfaces/decorator-options/expose-options.interface.ts",
    "chars": 1109,
    "preview": "/**\n * Possible transformation options for the @Expose decorator.\n */\nexport interface ExposeOptions {\n  /**\n   * Name o"
  },
  {
    "path": "src/interfaces/decorator-options/transform-options.interface.ts",
    "chars": 1004,
    "preview": "/**\n * Possible transformation options for the @Transform decorator.\n */\nexport interface TransformOptions {\n  /**\n   * "
  },
  {
    "path": "src/interfaces/decorator-options/type-discriminator-descriptor.interface.ts",
    "chars": 792,
    "preview": "import { ClassConstructor } from '..';\n\n/**\n * Discriminator object containing the type information to select a proper t"
  },
  {
    "path": "src/interfaces/decorator-options/type-options.interface.ts",
    "chars": 566,
    "preview": "import { DiscriminatorDescriptor } from './type-discriminator-descriptor.interface';\n\n/**\n * Possible transformation opt"
  },
  {
    "path": "src/interfaces/index.ts",
    "chars": 787,
    "preview": "export * from './decorator-options/expose-options.interface';\nexport * from './decorator-options/exclude-options.interfa"
  },
  {
    "path": "src/interfaces/metadata/exclude-metadata.interface.ts",
    "chars": 516,
    "preview": "import { ExcludeOptions } from '..';\n\n/**\n * This object represents metadata assigned to a property via the @Exclude dec"
  },
  {
    "path": "src/interfaces/metadata/expose-metadata.interface.ts",
    "chars": 511,
    "preview": "import { ExposeOptions } from '..';\n\n/**\n * This object represents metadata assigned to a property via the @Expose decor"
  },
  {
    "path": "src/interfaces/metadata/transform-fn-params.interface.ts",
    "chars": 273,
    "preview": "import { TransformationType } from '../../enums';\nimport { ClassTransformOptions } from '../class-transformer-options.in"
  },
  {
    "path": "src/interfaces/metadata/transform-metadata.interface.ts",
    "chars": 642,
    "preview": "import { TransformOptions } from '..';\nimport { TransformFnParams } from './transform-fn-params.interface';\n\n/**\n * This"
  },
  {
    "path": "src/interfaces/metadata/type-metadata.interface.ts",
    "chars": 713,
    "preview": "import { TypeHelpOptions, TypeOptions } from '..';\n\n/**\n * This object represents metadata assigned to a property via th"
  },
  {
    "path": "src/interfaces/target-map.interface.ts",
    "chars": 344,
    "preview": "/**\n * Allows to specify a map of Types in the object without using @Type decorator.\n * This is useful when you have ext"
  },
  {
    "path": "src/interfaces/type-help-options.interface.ts",
    "chars": 171,
    "preview": "// TODO: Document this interface. What does each property means?\nexport interface TypeHelpOptions {\n  newObject: any;\n  "
  },
  {
    "path": "src/storage.ts",
    "chars": 433,
    "preview": "import { MetadataStorage } from './MetadataStorage';\nimport { getGlobal } from './utils';\n\nconst globalScope = getGlobal"
  },
  {
    "path": "src/utils/get-global.util.spect.ts",
    "chars": 409,
    "preview": "import { getGlobal } from '.';\n\ndescribe('getGlobal()', () => {\n  it('should return true if Buffer is present in globalT"
  },
  {
    "path": "src/utils/get-global.util.ts",
    "chars": 960,
    "preview": "/**\n * This function returns the global object across Node and browsers.\n *\n * Note: `globalThis` is the standardized ap"
  },
  {
    "path": "src/utils/index.ts",
    "chars": 70,
    "preview": "export * from './get-global.util';\nexport * from './is-promise.util';\n"
  },
  {
    "path": "src/utils/is-promise.util.ts",
    "chars": 136,
    "preview": "export function isPromise<T>(p: any): p is Promise<T> {\n  return p !== null && typeof p === 'object' && typeof p.then =="
  },
  {
    "path": "test/functional/basic-functionality.spec.ts",
    "chars": 52674,
    "preview": "import 'reflect-metadata';\nimport {\n  instanceToInstance,\n  classToClassFromExist,\n  instanceToPlain,\n  classToPlainFrom"
  },
  {
    "path": "test/functional/circular-reference-problem.spec.ts",
    "chars": 3708,
    "preview": "import 'reflect-metadata';\nimport { instanceToInstance, instanceToPlain, plainToInstance } from '../../src/index';\nimpor"
  },
  {
    "path": "test/functional/custom-transform.spec.ts",
    "chars": 22834,
    "preview": "/* eslint-disable @typescript-eslint/camelcase */\nimport 'reflect-metadata';\nimport {\n  instanceToInstance,\n  instanceTo"
  },
  {
    "path": "test/functional/default-values.spec.ts",
    "chars": 1692,
    "preview": "import { Expose, plainToInstance, Transform } from '../../src';\n\ndescribe('expose default values', () => {\n  class User "
  },
  {
    "path": "test/functional/es6-data-types.spec.ts",
    "chars": 9626,
    "preview": "import 'reflect-metadata';\nimport { instanceToPlain, plainToInstance, Expose } from '../../src/index';\nimport { defaultM"
  },
  {
    "path": "test/functional/ignore-decorators.spec.ts",
    "chars": 918,
    "preview": "import 'reflect-metadata';\nimport { instanceToPlain } from '../../src/index';\nimport { defaultMetadataStorage } from '.."
  },
  {
    "path": "test/functional/implicit-type-declarations.spec.ts",
    "chars": 3882,
    "preview": "import 'reflect-metadata';\nimport { plainToInstance } from '../../src/index';\nimport { defaultMetadataStorage } from '.."
  },
  {
    "path": "test/functional/inheritence.spec.ts",
    "chars": 1240,
    "preview": "import 'reflect-metadata';\nimport { plainToInstance, Transform, Type } from '../../src/index';\nimport { defaultMetadataS"
  },
  {
    "path": "test/functional/prevent-array-bomb.spec.ts",
    "chars": 1942,
    "preview": "import 'reflect-metadata';\nimport { plainToInstance } from '../../src/index';\nimport { defaultMetadataStorage } from '.."
  },
  {
    "path": "test/functional/promise-field.spec.ts",
    "chars": 2194,
    "preview": "import 'reflect-metadata';\nimport { defaultMetadataStorage } from '../../src/storage';\nimport { plainToInstance, Type, i"
  },
  {
    "path": "test/functional/serialization-deserialization.spec.ts",
    "chars": 3505,
    "preview": "import 'reflect-metadata';\nimport { deserialize, deserializeArray, serialize } from '../../src/index';\nimport { defaultM"
  },
  {
    "path": "test/functional/specify-maps.spec.ts",
    "chars": 45867,
    "preview": "import 'reflect-metadata';\nimport {\n  instanceToInstance,\n  classToClassFromExist,\n  instanceToPlain,\n  classToPlainFrom"
  },
  {
    "path": "test/functional/transformation-option.spec.ts",
    "chars": 4977,
    "preview": "import 'reflect-metadata';\nimport { instanceToPlain, plainToInstance } from '../../src/index';\nimport { defaultMetadataS"
  },
  {
    "path": "test/functional/transformer-method.spec.ts",
    "chars": 5644,
    "preview": "import 'reflect-metadata';\nimport { defaultMetadataStorage } from '../../src/storage';\nimport {\n  Exclude,\n  Expose,\n  T"
  },
  {
    "path": "test/functional/transformer-order.spec.ts",
    "chars": 1181,
    "preview": "import 'reflect-metadata';\nimport { plainToInstance } from '../../src/index';\nimport { defaultMetadataStorage } from '.."
  },
  {
    "path": "tsconfig.json",
    "chars": 526,
    "preview": "{\r\n  \"compilerOptions\": {\r\n    \"module\": \"commonjs\",\r\n    \"moduleResolution\": \"node\",\r\n    \"target\": \"es2018\",\r\n    \"lib"
  },
  {
    "path": "tsconfig.prod.cjs.json",
    "chars": 120,
    "preview": "{\n  \"extends\": \"./tsconfig.prod.json\",\n  \"compilerOptions\": {\n    \"module\": \"CommonJS\",\n    \"outDir\": \"build/cjs\"\n  },\n}"
  },
  {
    "path": "tsconfig.prod.esm2015.json",
    "chars": 123,
    "preview": "{\n  \"extends\": \"./tsconfig.prod.json\",\n  \"compilerOptions\": {\n    \"module\": \"ES2015\",\n    \"outDir\": \"build/esm2015\",\n  }"
  },
  {
    "path": "tsconfig.prod.esm5.json",
    "chars": 141,
    "preview": "{\n  \"extends\": \"./tsconfig.prod.json\",\n  \"compilerOptions\": {\n    \"module\": \"ES2015\",\n    \"target\": \"ES5\",\n    \"outDir\":"
  },
  {
    "path": "tsconfig.prod.json",
    "chars": 110,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"strict\": false,\n    \"declaration\": false,\n  },\n}"
  },
  {
    "path": "tsconfig.prod.types.json",
    "chars": 155,
    "preview": "{\n  \"extends\": \"./tsconfig.prod.json\",\n  \"compilerOptions\": {\n    \"declaration\": true,\n    \"emitDeclarationOnly\": true,\n"
  },
  {
    "path": "tsconfig.spec.json",
    "chars": 237,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"strict\": false,\n    \"strictPropertyInitialization\": false,"
  }
]

About this extraction

This page contains the full source code of the typestack/class-transformer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (279.1 KB), approximately 72.6k tokens, and a symbol index with 276 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!