Wanna see something cool? Check out Angular Spotify 🎧

Casting a JSON object to a TypeScript class

TL;DR - Use class-transformer to transform JSON object to class instance.

Working Example

Problem

I started working with TypeScript about two years ago. Most of the time I read a JSON object from a remote REST server. This JSON object has all the properties of a TypeScript class. There is a question always buzz my head: How do I cast/parse the received JSON object to an instance of a corresponding class?.

Over time, I found this class-transformer library is super helpful. You can install this into your project and start using it from now on to see the difference. They supported nested property too so you don’t have to worry about that.

It is worth mentioning that not all the time we need to cast from JSON object to a class, but sometimes it is really helpful. As per the example above, If I don’t have the method getFullName in the instance of class, I could create a new util method that takes a plain User object as argument and return the expected result. In my projects, many times I used visitor pattern for handling different concrete classes, and it is required the instance of each class to be working. It won’t work for a plain object. The decision is yours. Put all properties and methods inside a single class is the encapsulation in Object-Oriented Programming (OOP) while Functional Programming treated everything as a function.

The below section was quoted from their readme.

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. E.g var obj = {} Class objects are instances of classes with their 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 a JSON file. After you JSON.parse, it gives you a plain JavaScript object, not an instance of a class you have.

For example you have a list of users that you received from the server:

[
  {
    "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 defined on client side:

export class User {
  id: number
  firstName: string
  lastName: string
  age: number

  getFullName() {
    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('/api/users').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].getFullName() or users[0].isAdult() because “users” actually is array of plain javascript objects, not instances of User object. You lied to the 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? The 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. The purpose of this library is to help you to map you plain javascript objects to the instances of classes you have.

This library also great for models exposed in your APIs, because it provides a great tooling to control what your models are exposing in your API. Here is example how it will look like:

fetch('/api/users').then((users: Object[]) => {
  const realUsers = plainToClass(User, users)
  // now each user in realUsers is instance of User class
})

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

This is the result as my console.log in the example does. You could clearly see the type of the object

Casting a JSON object to a TypeScript class

Working with nested objects

When you are trying to transform objects that have nested objects, its required to know 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 contains. This is done using @Type decorator.

Let’s 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
Published 26 Apr 2019

Read more

 — Freeze screen in Chrome debugger / DevTools panel for inspect element that will disappear on hover/click
 — Angular render recursive view using *ngFor and ng-template
 — Skiing in Singapore - a coding diversion
 — npm - Check and update package if needed
 — CSS Layout - Horizontal & Vertical Align

Follow @trungvose on Twitter for more!