24

I'm using a JS library, specifically select2 that acts a tad differently than I'd like if the objects I'm passing it aren't plain objects. This is all checked by using jQuery's isPlainObject function.

Does TypeScript have a cast I'm unaware of that would achieve this without resorting to writing my own?

class Opt {
    constructor(public id, public text) {

    }

    toPlainObj(): Object {
        return {
            id: this.id,
            text: this.text
        }
    }
}

let opts = [
    new Opt(0, 'foo'),
    new Opt(1, 'bar')
];

console.clear()

console.log('both should be false')
$.map(opts, opt => {
    console.log($.isPlainObject(opt))
})

console.log('both should be true')
$.map(opts, opt => {
    console.log($.isPlainObject(opt.toPlainObj()))
})
self.
  • 1,612
  • 4
  • 18
  • 35

5 Answers5

39

You can use Object.assign():

class Point {
    private x: number;
    private y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    getX(): number {
        return this.x;
    }

    getY(): number {
        return this.y;
    }
}

let p1 = new Point(4, 5);
let p2 = Object.assign({}, p1);

p1 is the class instance, and p2 is just { x: 4, y: 5 }.

And with the toPlainObj method:

class Point {
    private x: number;
    private y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    getX(): number {
        return this.x;
    }

    getY(): number {
        return this.y;
    }

    toPlainObj(): { x: number, y: number } {
        return Object.assign({}, this);
    }
}

If this is something you need in more classes then you can have a base class which has this method:

class BaseClass<T> {
    toPlainObj(): T {
        return Object.assign({}, this);
    }
}

class Point extends BaseClass<{ x: number, y: number }> {
    private x: number;
    private y: number;

    constructor(x: number, y: number) {
        super();

        this.x = x;
        this.y = y;
    }

    getX(): number {
        return this.x;
    }

    getY(): number {
        return this.y;
    }
}
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • Excellent! I was just about to blow a fuse, but this answer saved me from that :) – MartinJH Apr 23 '18 at 10:26
  • I'm looking for the exact opposite. Have plain js object, want to convert it to a specific type. – Moulde Sep 12 '18 at 14:01
  • 1
    @Moulde something like this: https://stackoverflow.com/questions/43314791/how-to-instantiate-an-object-in-typescript-by-specifying-each-property-and-its-v ? – Nitzan Tomer Sep 12 '18 at 14:29
  • 4
    It is worth mentioning that this will not convert any of the objects properties into JS objects, they will remain as instances of whichever type they are. See the following example: https://www.typescriptlang.org/play/#code/MYGwhgzhAECC0G8C+AoFpIwEKJdaADgK4BGIAlsNAG5ggBcc0AvNAHYCmA7nABQCUAbhSoUIDgBdoADxbtu0LAOEo6HAE4Tes8mwgSwbYBwD2AM0VDoAemvQAKuqIdV4zdoB0tENF37DxuZwVrYOTi5iktAAnnIA8iQAVhzAEh6Y5ADmbLzIADQyQmhq7rF+BkamFlghdgBidBAuJVrRXnS+ehWBFrC1Yc4oQA – Qyaffer Sep 30 '19 at 13:12
  • @Qyaffer do you know of any way to also convert object instance properties to plain objects? – mattbeiswenger Jul 23 '20 at 15:53
10

Something like this is simple and it works :

let plainObj;
try {
    plainObj = JSON.parse(JSON.stringify(obj));
} catch(e) {
    console.error(e)
}
Alain Beauvois
  • 5,896
  • 3
  • 44
  • 26
2

You just need to destructure the object. Something like this: {...myObject}

0

Lodash internal functions are unsung heros. They are not exported by default. _baseClone can do that. I do this in my angular project.

import * as baseClone from 'lodash/_baseClone'

const classObj = getComplexClassObject();
const flatObj = baseClone(classObj, 7);

7 because 1 | 2 | 4. Deep Flag, Flat Flag, Symbols Flag, respectively. Check it out, _baseClone.js.

m0hithreddy
  • 1,752
  • 1
  • 10
  • 17
-1

I know this is an old post, however in Nitzan's answer above, the typescript syntax checker seems to be ok with just doing the following:

const plainObj:any = ClassObj;
ClassObj.newProperty = "Test";

So it seems like there is no real need to use Object.assign unless there is a difference I am unaware of.