0

In an effort to properly instantiate Typescript objects from data received over HTTP as JSON, I was exploring the possibility of using the for..in loop coupled with .hasOwnProperty() like so:

class User {
    private name: string;
    private age: number;

    constructor(data: JSON) {
        console.log('on constructor\ndata', data);

        for (var key in data) {
            console.log('key:', key);
            if (User.hasOwnProperty(key)) {
                console.log('User has key:', key);
                this[key] = data[key];
            }
        }
    }

    displayInfo(): string{
        return JSON.stringify(this);
    }
}

let button = document.createElement('button');
button.textContent = "Test";
button.onclick = () => {
    try{
        let json = JSON.parse('{"name": "Zorro","age": "24"}');
        let usr = new User(json);
        console.log(usr.displayInfo());
    }catch (error){
        alert(error);
    }

}

document.body.appendChild(button);

Using similar code in my project fails completely. That is expected as the compiled JS code has no awareness of the private TS vars and so, hasOwnProperty is always false.

However, I was using the Typescript Playground, and running that code there produces the following output in the console:

on constructor
data Object {name: "Zorro", age: "24"}
key: name
User has key: name
key: age
{"name":"Zorro"}

As you can see, there are clearly some unexpected things happening here. The first key is recognized and the new User instance is initialized with the value from the JSON, yet that does not happen for the second key.

Can someone explain why this happens?

Endless
  • 34,080
  • 13
  • 108
  • 131
tiansivive
  • 506
  • 1
  • 4
  • 16
  • 1
    Are you sure you mean `User.hasOwnProperty` not `this.hasOwnProperty` or `data.hasOwnProperty`? Notice that `hasOwnProperty` is not a class reflection method, it's a on object method. – Bergi Sep 07 '16 at 17:21
  • 1
    I agree with @Bergi - Calling `User` will call the class statically. While you could check prototyped properties that way, you would not be checking the properties of an object that has been instantiated. – Jim Sep 07 '16 at 17:46
  • @Bergi @Jim I do not want to check the properties of the object being instantiated, that would always return false as I call `hasOwnProperty` before actually setting the property values. That'd be the situation using `this`. What I was trying to do is check if the key from the JSON exists in the class, not the instance, and only then set it to the value from the JSON. This was more of a fail-safe as since I control the JSON structure I don't actually need this check, but I experimented and got this result, which confused me – tiansivive Sep 07 '16 at 18:02
  • @TiagoVilaVerde: There is no class. `User` is the constructor, and it only has the properties `name`, `prototype`, and `length`. – Bergi Sep 07 '16 at 18:46
  • You probably should simply do `this.name = data.name; this.age = data.age` and drop any enumerations or existence checks. – Bergi Sep 07 '16 at 18:48

2 Answers2

2

As was pointed out in the comments, you should be using this.hasOwnProperty instead of User.hasOwnProperty. And as you noticed, this code is busted anyway because the property declarations in the class don't actually create own properties on the object (they would need to be initialized for this to happen).

But why did you get a hit on the name key? The User object is a constructor function for your class. Functions do have a name property:

function fn() { }
console.log(fn.name); // prints 'fn'

They don't have an age property, of course.

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
0

Your constructor would of course just have to look like this, if you want to construct User instances from plain JavaScript objects:

constructor(data: any) {
    this.name = data.name;
    this.age = data.age;
}
Jakob Kruse
  • 2,458
  • 18
  • 17