MCPcopy
hub / github.com/typestack/class-transformer

github.com/typestack/class-transformer @v0.5.1 sqlite

repository ↗ · DeepWiki ↗ · release v0.5.1 ↗
198 symbols 421 edges 71 files 2 documented · 1%
README

class-transformer

Build Status codecov npm version

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. Source code is available here.

Table of contents

What is class-transformer

In JavaScript there are two types of objects:

  • plain (literal) objects
  • class (constructor) objects

Plain objects are objects that are instances of Object class. Sometimes they are called literal objects, when created via {} notation. Class objects are instances of classes with own defined constructor, properties and methods. Usually you define them via class notation.

So, what is the problem?

Sometimes you want to transform plain javascript object to the ES6 classes you have. For example, if you are loading a json from your backend, some api or from a json file, and after you JSON.parse it you have a plain javascript object, not instance of class you have.

For example you have a list of users in your users.json that you are loading:

[
  {
    "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:

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:

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:

fetch('users.json').then((users: Object[]) => {
  const realUsers = plainToClass(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

Node.js

  1. Install module:

npm install class-transformer --save

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

  1. 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

  1. Install module:

npm install class-transformer --save

  1. 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

```

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

  1. 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

plainToClass

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

import { plainToClass } from 'class-transformer';

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

plainToClassFromExist

This method transforms a plain object into an instance using an already filled Object which is an instance of the target class.

const defaultUser = new User();
defaultUser.role = 'user';

let mixedUser = plainToClassFromExist(defaultUser, user); // mixed user should have the value role = user when no value is set otherwise.

classToPlain

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

import { classToPlain } from 'class-transformer';
let photo = classToPlain(photo);

classToClass

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

import { classToClass } from 'class-transformer';
let photo = classToClass(photo);

You can also use an ignoreDecorators option in transformation options to ignore all decorators you classes is using.

serialize

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

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

serialize works with both arrays and non-arrays.

deserialize and deserializeArray

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

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

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

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

Enforcing type-safe instance

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

import { plainToClass } from 'class-transformer';

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

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

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

// User {
//   unkownProp: 'hello there',
//   firstName: 'Umed',
//   lastName: 'Khudoiberdiev',
// }

If this behaviour does not suit your needs, you can use the excludeExtraneousValues option in the plainToClass method while exposing all your class properties as a requirement.

import { Expose, plainToClass } 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(plainToClass(User, fromPlainUser, { excludeExtraneousValues: true }));

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

Working with nested objects

When you are trying to transform objects that have nested objects, 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:

import { Type, plainToClass } from 'class-transformer';

export class Album {
  id: number;

  name: string;

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

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

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

Providing more than one type option

In case the nested object can be of different types, you can provide an additional options object, that specifies a discriminator. The discriminator option must define a property that holds the 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:

{
  "id": 1,
  "name": "foo",
  "topPhoto": {
    "id": 9,
    "filename": "cool_wale.jpg",
    "depth": 1245,
    "__type": "underwater"
  }
}
import { Type, plainToClass } 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 = plainToClass(Album, albumJson);
// now album is Album object with a UnderWater object without `__type` property.

Hint: The same applies for arrays with different sub types. Moreover you can specify keepDiscriminatorProperty: true in the options to keep the discriminator property also inside your resulting class.

Exposing getters and method return values

You can expose what your getter or method return by setting an @Expose() decorator to those getters or methods:

import { Expose } from 'class-transformer';

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

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

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

Exposing properties with different names

If you want to expose some of the properties with a different nam

Extension points exported contracts — how you extend this code

ClassTransformOptions (Interface)
(no doc)
src/interfaces/class-transformer-options.interface.ts
TargetMap (Interface)
(no doc)
src/interfaces/target-map.interface.ts
TypeHelpOptions (Interface)
(no doc)
src/interfaces/type-help-options.interface.ts
DiscriminatorDescriptor (Interface)
(no doc)
src/interfaces/decorator-options/type-discriminator-descriptor.interface.ts
TypeOptions (Interface)
(no doc)
src/interfaces/decorator-options/type-options.interface.ts

Core symbols most depended-on inside this repo

plainToInstance
called by 92
src/index.ts
Expose
called by 86
src/decorators/expose.decorator.ts
clear
called by 79
src/MetadataStorage.ts
Type
called by 79
src/decorators/type.decorator.ts
instanceToPlain
called by 71
src/index.ts
Exclude
called by 53
src/decorators/exclude.decorator.ts
Transform
called by 29
src/decorators/transform.decorator.ts
instanceToInstance
called by 23
src/index.ts

Shape

Class 107
Method 54
Function 23
Interface 13
Enum 1

Languages

TypeScript100%

Modules by API surface

src/MetadataStorage.ts21 symbols
test/functional/custom-transform.spec.ts16 symbols
test/functional/basic-functionality.spec.ts12 symbols
src/index.ts11 symbols
src/TransformOperationExecutor.ts11 symbols
src/ClassTransformer.ts11 symbols
test/functional/transformer-method.spec.ts8 symbols
test/functional/specify-maps.spec.ts8 symbols
test/functional/promise-field.spec.ts6 symbols
test/functional/inheritence.spec.ts6 symbols
test/functional/es6-data-types.spec.ts6 symbols
test/functional/circular-reference-problem.spec.ts6 symbols

Dependencies from manifests, versioned

@rollup/plugin-commonjs21.0.1 · 1×
@rollup/plugin-node-resolve13.0.6 · 1×
@types/jest27.0.3 · 1×
@types/node16.11.9 · 1×
@typescript-eslint/eslint-plugin4.33.0 · 1×
@typescript-eslint/parser4.33.0 · 1×
eslint7.32.0 · 1×
eslint-config-prettier8.3.0 · 1×
eslint-plugin-jest25.2.4 · 1×
husky4.3.8 · 1×
jest26.6.3 · 1×
lint-staged12.1.2 · 1×

For agents

$ claude mcp add class-transformer \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact