简体   繁体   中英

Mapping JSON String/JS object to Typescript class with private attributes

This is a typescript class with an exposed but not editable attribute:

export class Category {

    private _id: number;

    constructor(id: number){
        this._id = id;
    }

    public get id(){
        return this._id;
    }

}

I would like to map it from a JSON like this:

{ id: 1 }

There are some obvious problems here:

  • I know that "_id" can't be magically mapped to "id" so maybe I could implement a custom logic that renames all the attributes with a _ before the name
  • I would like to keep the constructor with the id param but maybe I'm not able to map an object who require arguments before instantiation
  • With an empty constructor, I tried Object.assign(new Category(), jsonObject) , however, this does not work since Cannot set property id of #<Category> which has only a getter

What I want to avoid is to write custom mapping logic for every class like this with private attributes, I tried some other solutions and libraries but didn't work for my situation, they're all referencing to class with only public attributes

I don't even know if what I ask is achievable, so if the case it isn't, then I will "surrender" to use the class with only public attributes

The missconception here is that you need a getter/setter at all just to manage visibility. You can't prevent code from accessing and modifying id , no matter what you do. You can however tell the IDE (and the developer using it) that he can only read/get the property by using the readonly modifier, which simplifies your code to:

 export class Category {
   readonly id: number;
 }

Noe that readonly thing only exists at compile time, and doesnt have any affects at runtime. Your IDE will prevent you from doing:

 (new Category).id = 5;

But it allows you to easily do:

 const category = Object.assign(new Category, { id: 5 });

Pass the object through constructor:

export class Category {
    private _a: number;
    private _b: string;
    constructor(values: { a: number; b: string }){
        this._a = values.a;
        this._b = values.b;
    }
    public getA() { return this._a; }
    public getB() { return this._b; }
}

You can still call it with or without JSON:

let cat1 = Category (values: { a: 42, b: 'hello' });

let json = '{"a":42,"b":"hello"}'
let cat2 = Category (values: JSON.parse (json));

Alternatively, keep the values in an object rather than a direct member. This makes it unnecessary to map the object to member variables:

export class Category {
    private _values: {
        a: number;
        b: string;
    }
    constructor(values: { a: number; b: string }){
        this._values = values;
    }
    public getA() { return this._values.a; }
    public getB() { return this._values.b; }
}

If you want to keep the old constructor, you can do so like this:

export class Category {
    private _values: {
        a: number;
        b: string;
    }
    constructor(a: number, b: string) {
        this._values = { a: a, b: b };
    }
    public static make (values: { a: number; b: string }) {
        this._values = values;
    }
    public getA() { return this._values.a; }
    public getB() { return this._values.b; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM