DEV Community

loading...
Cover image for Simple way to serialize objects to JSON in TypeScript

Simple way to serialize objects to JSON in TypeScript

Hans Ott
🚀 Startup co-founder 🎹 Piano & guitar student ⛷ Avid skier 💡 Strong interest in domain-driven design and functional programming
・2 min read

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.

class-transformer depends on the reflect-metadata package. Read this blogpost to know why you should be careful with depending on this package.

io-ts requires knowledge of some functional programming concepts. While I'm a big fan of functional programming, it's not for everyone. (The library is very well designed though)

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

We want to keep our dependencies as small as possible.

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;
  }
}
Enter fullscreen mode Exit fullscreen mode
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)
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
const user = new User("id", new Email("john.doe@acme.com"));
const json = user.serialize();
Enter fullscreen mode Exit fullscreen mode
const json = await redis.get(key);
const user = User.fromSerialized(json);
Enter fullscreen mode Exit fullscreen mode
  • 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! 😊

Discussion (0)