1

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.namebut 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

Javier Enríquez
  • 630
  • 1
  • 9
  • 25

3 Answers3

2

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);
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • I think the question is how to convert json data to a particular class instance. Something like that should work (assuming there's a user's constructor), but gives me error when I use the result in ngIf return this._http.get(this.urll) .map((res: Response) => res.json()) .map(obj:any => {return new User(obj.number, obj.name, obj.email);}) – rook Sep 10 '16 at 17:53
  • @rook There's no need for using `map` twice.. this should work: `this._http.get(this.urll).map((res: Response) => { let json = res.json(); return new User(json.id, json.name, json.email); })`. What error do you get? – Nitzan Tomer Sep 10 '16 at 17:57
  • Yes, it's shorter but the error is the same: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays. – rook Sep 10 '16 at 18:04
  • @rook I suggest that you post a new question with your error along with your code. It's hard to understand code/problems in the comments, plus with a new question you'll have more people to help except for me. Also, it's not related to this question or my answer. – Nitzan Tomer Sep 10 '16 at 18:08
2

Nitzan pretty much explained the theory behind this, so I'll just provide an alternative approach:

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

class User {
   userInfo: UserInfo;
   constructor(userInfo: UserInfo) {
       this.userInfo = userInfo;
   }
   getUpperCaseName(): string {
       return this.userInfo.name.toLocaleUpperCase();
   }
}

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

let user: User = new User(json);
tedcurrent
  • 431
  • 3
  • 13
0

There is a problem when your User has 50 or more properties...

Add a constructor in your User object so that it extends your json object.

export class User {
    constructor( jsonUser: any )
    {
      $.extend(this, jsonUser);
    }
    id : number;
    name : string;
    email : string;
    getUpperCaseName() {...}
}

In your ajax callback, create the User object from your json Object:

let receivedUser = new User( jsonUser );
let userName = receivedUser.getUpperCaseName();

I detailed the solution in that post.

Community
  • 1
  • 1
Anthony Brenelière
  • 60,646
  • 14
  • 46
  • 58