6

I've JSON string

const json = '{ "first_name": "John", "last_name": "Doe"}';

and a class User

class User {

  constructor(
    public first_name: string,
    public last_name: string
  ) {
  }

  public getFullName = (): string => {
    return this.first_name + ' ' + this.last_name;
  };
}

and when I try this

const user: User = JSON.parse(json);

console.log(user.first_name); // Works, prints "John"
console.log(user.last_name); // Works, prints "Doe"
console.log(user.getFullName()); //Error: TypeError: user.getFullName is not a function

I know the error happened, because JSON.parse simply matched the User type but not methods in it.

So to fix this, I wrote a fromJSON static method in User class to parse manually,

public static fromJSON = (json: string): User => {
    const jsonObject = JSON.parse(json);
    return new User(
      jsonObject.first_name,
      jsonObject.last_name
    );
};

So these are my questions

  1. Is this a perfect solution ?
  2. Is there any better solution than this ?
  3. Is there any built-in solution for this in TypeScript?
theapache64
  • 10,926
  • 9
  • 65
  • 108
  • Maybe the real question is why you want to create `new User()` instances for JSON data? Wouldn't it be enough to have the JSON available as an array in your application? – Kokodoko Jun 28 '18 at 11:24

1 Answers1

5

It's a decent solution. A better one would be to use Object.assign so as to not have to write all the assignments as this can get out of hand if there are many properties, but either will work fine.

class User {

    public first_name!: string;
    public last_name!: string;
    constructor(data: Partial<User>) {
        Object.assign(this, data);
    }

    public getFullName (): string {
        return this.first_name + ' ' + this.last_name;
    };

    public static fromJSON = (json: string): User => {
        const jsonObject = JSON.parse(json);
        return new User(jsonObject);
    };  
}

There is no built-in solution for this in typescript, Object.assign is the closest to it. You might consider changing the constructor dynamically as detailed here, that will convert the object to an instance of the class but will not necessarily perform better.

Gorgsenegger
  • 7,356
  • 4
  • 51
  • 89
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • Ok. I'll look into it. – theapache64 Jun 28 '18 at 08:28
  • I've found a [github library](https://github.com/JohnWeisz/TypedJSON) which uses `TypeScript Decorator`. Is this worth trying ? What do you think ? – theapache64 Jun 28 '18 at 08:31
  • @theapache64 My understanding of this library is that it validates that the json object conforms to the interface if decorators are used. Also it seems to have support for nested objects which you may want. So, worth a look, depending on our use case might provide some useful functionality – Titian Cernicova-Dragomir Jun 28 '18 at 08:37
  • Yes. I have deep json object structures in my real-scenario. Thank you for the response Titian – theapache64 Jun 28 '18 at 08:39
  • `Object.assign` does not work when targeting ES5. You might want to factor [this Q&A](https://stackoverflow.com/questions/41144939/what-to-use-instead-of-object-assign-in-typescript) into your reply. – Sefe Jun 28 '18 at 08:50
  • Nice solution! I didn't know about Partial. If you don't mind a small addition: class methods can be written as `public getFullName(): string { ... }` you don't need the ` = () =>` – Kokodoko Jun 28 '18 at 11:19
  • @Kokodoko 10x for the catch, I just coped over the code from the question and didn't notice that part as it wasn't core to the answer. Fixed :) – Titian Cernicova-Dragomir Jun 28 '18 at 12:14
  • After hours of research this was the only real helpful answer on StackOverflow - thank you for that. – schlenger May 12 '21 at 11:00