2

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

Saeed Hassanvand
  • 931
  • 1
  • 14
  • 31
Tizio Fittizio
  • 520
  • 5
  • 16
  • Why that getter/setter? Wjy not just `readonly id: number;` ? – Jonas Wilms Nov 05 '18 at 13:23
  • @JonasWilms well i was just following https://stackoverflow.com/questions/12827266/get-and-set-in-typescript, why i should use readonly instead? – Tizio Fittizio Nov 05 '18 at 13:26
  • Can't you just pass the JSON via constructor instead of just the id? – digory doo Nov 05 '18 at 13:29
  • @digorydoo since i can have only one constructor per class, i should write a static method for every class to be instantiated in the "normal" way, i would like to preserve the common instantiation with params – Tizio Fittizio Nov 05 '18 at 13:33

2 Answers2

2

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 });
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • There's a subtle difference between private and readonly, with readonly i can assing value only once in the constructor, this means that i'm not able to edit that attribute anymore after instantiation, i want to keep it editable in the class, in short words i would like to keep the "private attribute" concept here – Tizio Fittizio Nov 05 '18 at 15:09
  • @tizio in that case you could bypass the modifier `(this as any).id = 5;` – Jonas Wilms Nov 06 '18 at 06:00
1

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; }
}
digory doo
  • 1,978
  • 2
  • 23
  • 37