Javier Enríquez Javier Enríquez - 2 months ago 8
TypeScript Question

Typescript object from JSON

I'm using TypeScript to build an app and I'm making API calls to retrieve objects. For instance, I have a TypeScript

User
Object like this:

export class User {
id : number;
name : string;
email : string;
}


And my API returns

{
"id" : 3,
"name" : "Jonn",
"email" : "john@example.com"
}


I want to convert that JSON to a
User
. I've read in another posts I can do this:

let user : User = <User> myJson;


This seemly works. I can access properties of the user like
user.name
but my problem is that, if the
User
class implements some method, the properties are not available. For example, if inside the
User
class I have this:

getUppercaseName() : string {
return this.name.toUppercase();
}


This happens:
user.name
returns
John
but
user.getUppercaseName()
returns
undefined


What's going on? How to solve this

Answer

What you are doing it treating classes as interfaces, as this will work exactly the same:

export interface User {
    id : number;
    name : string;
    email : string;
}

The reason that the compiler doesn't complain about you using classes this way is because:

One of TypeScript’s core principles is that type-checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”

(read more about duck typing)

Or with an example:

class User {
    id: number;
    name: string;
    email: string;

    constructor(id: number, name: string, email: string) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
}

function logUser(user: User) {
    console.log(`user id: ${ user.id }, name: ${ user.name }, email: ${ user.email }`);
}

logUser({
    id: 1,
    name: "user 1",
    email: "mailaddress"
});

logUser(new User(2, "user 2", "anotheraddress"));

In the two calls to logUser I pass objects that satisfy the interface of the User class.

If you want to have an instance of that class instead of an object that satisfies it then you should do something like:

new User(myJson.id, myJson.name, myJson.email);

And have a constructor like in my example, or:

interface IUser {
    id: number;
    name: string;
    email: string;
}

class User implements IUser {
    id: number;
    name: string;
    email: string;

    constructor(data: IUser) {
        this.id = data.id;
        this.name = data.name;
        this.email = data.email;
    }
}

...
new User(myJson);
Comments