Search code examples
node.jstypescriptclass-transformer

Is it possible to handle unknown properties when creating a TS class instance from JSON?


When converting a plain object from JSON to a class instance, I need a way to catch all properties that have no corresponding class properties and store them in a some place (additionalData in the example below).

I looked into some libraries ( class-transformer, marshal.ts, TypedJSON) but there seems to be no means to do what I want.

Below is a hypothetical example of what I would like to achieve (it uses class-transformer but any other deserializer library would work for me)

// model/DailyStatsRecord.ts
export class DailyStatsRecord {
    public uuid: string;
    public date: string;

    public additionalData: any;
}
// index.ts
import "reflect-metadata";
import {plainToClass} from "class-transformer";
import {DailyStatsRecord} from './model/DailyStatsRecord';

const instance = plainToClass(DailyStatsRecord, {
    uuid: "faf9a028-5bbe-11ea-bc55-0242ac130003",
    date: "2020-03-01",
    otherField: 123,
    more: ["data"],
    foo: "bar",
});

console.log(JSON.stringify(instance.additionalData, null, 2));

Here is what I want this script to output:

{
  "otherField": 123,
  "more": ["data" ],
  "foo": "bar"
}

Solution

  • This can be accomplished without any third-party libraries, but you'll have to have pull the known properties out manually using object destructuring with a rest gather for the others and have the constructor take a parameter that is an object with the remaining key/values:

    type POJO = {
      [key: string]: any
    };
    
    class HasExtra {
      public foo: string;
      public bar: number;
      public rest: POJO;
      constructor(foo: string, bar: number, rest: POJO) {
        this.foo = foo;
        this.bar = bar;
        this.rest = rest;
      }
    }
    
    const dataFromJSON = {
      foo: "hi",
      bar: 3,
      baz: true,
      qux: null,
    }
    
    const {
      foo,
      bar,
      ...rest
    } = dataFromJSON;
    
    const instance = new HasExtra(foo, bar, rest);
    console.log(instance.rest); // logs { baz: true, qux: null }
    

    Link to playground