Simple way to serialize objects to JSON in TypeScript

When we’re building an application we often need to serialize objects to JSON for storing them in a key value store (e.g. Redis) or publishing them on a queue.

While there are libraries available like class-transformer or io-ts, there's an easier way to serialize/unserialize objects to JSON.

In both cases we also need to depend on an extra package.

What none of these packages offer is a way to deal with backwards compatibility (e.g. when you've added or renamed a property of an object).

I also want something simple.

Let's say we want to serialize a user with an email value object to JSON:

class Email {
  constructor(private readonly email: string) {
    if (!this.email.includes("@")) {
      throw new Error(`Not an email address: ${this.email}`);
    }
  }

  asString() {
    return this.email;
  }
}
class User {
  constructor(
    private readonly id: string,
    private readonly email: Email
  ) {
    if (!this.id) {
      throw new Error("User ID cannot be empty!");
    }
  }

  private toObject() {
    return {
      id: this.id,
      email: this.email.asString(),
    }
  }

  serialize() {
    return JSON.stringify(this.toObject());
  }

  static fromSerialized(serialized: string) {
    const user: ReturnType<User["toObject"]> = JSON.parse(serialized);

    return new User(
      user.id,
      new Email(user.email)
    );
  }
}
const user = new User("id", new Email("[email protected]"));
const json = user.serialize();
const json = await redis.get(key);
const user = User.fromSerialized(json);
  • We add a private toObject() method that returns a plain object for the User (since JSON is not aware of classes).
  • We add a serialize() method that returns the plain object as a JSON string.
  • We add a static unserialize(serialized: string) method to recreate the User instance. JSON.parse has any as return type, which results in no type checking or autocompletion. We can grab the return type of the toObject() method with ReturnType<User["toObject"]> to regain type checking/autocompletion.
  • Always guard against invalid state in your entities. This makes sure that a User always has an ID and a valid email address.
  • Look ma, no packages needed!

Let me know if this blogpost was useful! 😊

31