Search code examples
javascriptjsontypescriptstringifyjsonparser

How could I serialize/deserialize complex objects in Typescript such as the deserialized object is of the same type as the object that was serialized


I have a complex object that I want to serialize and deserialize and obtain an object of the same type.

let workflow = new Workflow();
console.log(`workflow is instanceof Workflow: ${workflow instanceof Workflow}`);
console.log(workflow);

let json = JSON.stringify(workflow);
console.log(json);

let workflow2 = JSON.parse(json) as Workflow;
console.log(workflow2);
console.log(`workflow2 is instanceof Workflow: ${workflow2 instanceof Workflow}`);

let workflow3: Workflow = JSON.parse(json) as Workflow;
console.log(workflow3);
console.log(`workflow3 is instanceof Workflow: ${workflow3 instanceof Workflow}`);

The console output is:

enter image description here

Is there an out of the box solution for this or I need to manually reinstantiate the complex object and set all of its properties?


Solution

  • You can use an object's fromJSON() together with a reviver function to JSON.parse() to achieve what you want.

    For example:

    type Serialized<T> = Pick<T, keyof T> & { _type: string };
    
    class Workflow {
      foo: number;
    
      constructor(foo: number) {
        this.foo = foo;
      }
    
      public toJSON(): Serialized<Workflow> {
        return {
          _type: this.constructor.name,
          ...this
        };
      }
    
      public static fromJSON(source: Serialized<Workflow>): Workflow {
        return new Workflow(source.foo);
      }
    }
    
    function reviver(key: string, value: any): any {
      if (typeof value === "object" && value && "_type" in value) {
        switch (value._type) {
          case "Workflow": return Workflow.fromJSON(value);
        }
      }
      return value;
    }
    
    const w = new Workflow(42);
    console.log(w instanceof Workflow);
    
    const s = JSON.stringify(w);
    console.log(s);
    
    const w2 = JSON.parse(s, reviver) as Workflow;
    console.log(w2.foo);
    console.log(w2 instanceof Workflow);
    

    prints:

    true
    {"_type":"Workflow","foo":42}
    42
    true
    

    Try it out yourself at the playground!