skip to Main Content

I have an instance of a class, say

class MyObject{
    id: number = 0;
    data: string = "";

    constructor(){

    }

    toString(){
        return `${this.id}: ${this.data}`;
    }

    
}

const myObj = new MyObject()
myObj.data = "hello"

How to convert it to a plain JS object:


const output = {id: 10, data: "hello"}

I know this works, but there must be a way without serializing the whole object:

const output = JSON.parse(JSON.stringify(myObj))

What is the best way to do this?

Note that I’m looking for a generic solution, not like this:

toObject() {
    return {
      id: this.id,
      data: this.data,
    };
  }

2

Answers


  1. To create an object with just the properties and not the methods of an instance, you can use Object.assign() to copy all the object’s enumerable own properties to another object, which can specify as a new, plain, object.

    class MyObject{
        id = 0;
        data = "";
    
        constructor(){
        }
    
        toString(){
            return `${this.id}: ${this.data}`;
        }
    
        
    }
    
    const instance = new MyObject();
    const properties = Object.assign({}, instance);
    console.log(properties);
    Login or Signup to reply.
  2. For simple cases you can use the object literal spread syntax:

    class MyObject{
        constructor(){
            this.id = 0;
            this.data = "";
        }
        toString(){
            return `${this.id}: ${this.data}`;
        }
    }
    
    const myObj = new MyObject();
    console.log(myObj.constructor.name); // MyObject
    myObj.data = "hello";
    const result = {...myObj};
    console.log(result.constructor.name); // Object

    If your original object has nested custom objects — that also need to convert to plain objects or arrays — then either make a recursive function, or use the structuredClone function (added in ECMAScript 2023, Node 17). Here is a more realistic example with a linked list implementation:

    class Node {
        constructor(value, next) {
            this.value = value;
            this.next = next;
        }
        *[Symbol.iterator]() {
            yield this.value;
            if (this.next) yield* this.next;
        }
    }
    
    class LinkedList {
        constructor(...values) {
            this.head = null;
            for (const value of values.toReversed()) {
                this.head = new Node(value, this.head);
            }
        }
        *[Symbol.iterator]() {
            if (this.head) yield* this.head
        }
        toString() {
            return [...this].join("→");
        }
    }
    
    const myObj = new LinkedList(1, 2, 3);
    console.log("list: ", myObj.constructor.name, myObj.toString());
    const result = structuredClone(myObj);
    console.log("list2: ", result.constructor.name, myObj);

    Note however that this does serialise/deserialise the whole object behind the scenes, and where native classes are involved, they are also maintained in the target object (like Set, Map, Date, RegExp, Number, …). This may or may not be what you want, but realise that JSON.stringify on such instances also has particular results: for a Set, Map or RegExp you get an empty object, for a Date you get a string, …etc, so this may need special care — depending on expectations.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search