Object Serialization in TypeScript: From JSON to protobuf
Serialization is the process of converting a complex data structure (object, array, class instance) into a format suitable for storage or transmission over a network. The reverse process — deserialization — restores the object from that format. In TypeScript, which is a strictly typed superset of JavaScript, serialization takes on special significance: it is important not only to transmit data but also to preserve types, methods, and class structure.
In this article, we will explore the main approaches and tools for object serialization in TypeScript, from built-in features to specialized libraries.
1. Native Serialization: JSON.stringify and JSON.parse
The simplest and most common method is using the built-in JSON.stringify() and JSON.parse() methods. However, this approach has limitations:
- Functions,
undefined,Symbol, andBigInt(in older versions) are not serialized. - The object prototype is not preserved — upon deserialization, you get a plain object, not a class instance.
- Circular references cause an error.
// Example of simple serializationinterface User { id: number; name: string; email: string;}
const user: User = { id: 1, name: "Ivan", email: "ivan@example.com"};
const jsonString = JSON.stringify(user);console.log(jsonString); // {"id":1,"name":"Ivan","email":"ivan@example.com"}
const parsedUser: User = JSON.parse(jsonString);console.log(parsedUser.name); // IvanFor simple data, this is sufficient, but if you need to work with classes, a more advanced solution is required.
2. Custom Serialization with toJSON and fromJSON Methods
TypeScript (and JavaScript) allows you to define a toJSON() method in a class, which will be called during serialization via JSON.stringify. For deserialization, you can create a static fromJSON method.
class Person { constructor( public firstName: string, public lastName: string, public age: number ) {}
// Method for serialization toJSON() { return { fullName: `${this.firstName} ${this.lastName}`, age: this.age }; }
// Static method for deserialization static fromJSON(json: any): Person { const [firstName, lastName] = json.fullName.split(' '); return new Person(firstName, lastName, json.age); }}
const person = new Person("Anna", "Petrova", 30);const json = JSON.stringify(person);console.log(json); // {"fullName":"Anna Petrova","age":30}
const restoredPerson = Person.fromJSON(JSON.parse(json));console.log(restoredPerson.firstName); // AnnaThis approach gives full control but requires writing boilerplate code for each class.
3. The class-transformer Library
class-transformer is one of the most popular libraries for serialization/deserialization in TypeScript. It allows you to transform plain objects into class instances and back using decorators.
Installation:
npm install class-transformer reflect-metadataUsage example:
import { plainToClass, classToPlain, Expose, Transform } from "class-transformer";import "reflect-metadata";
class Product { @Expose() id: number;
@Expose() name: string;
@Expose() @Transform(({ value }) => value.toUpperCase()) // custom transformation category: string;
@Expose({ name: "price_with_tax" }) // field renaming price: number;
constructor(id: number, name: string, category: string, price: number) { this.id = id; this.name = name; this.category = category; this.price = price; }
getInfo(): string { return `${this.name} (${th