4

I can't figure out how to map json object array returned from web service to correctly typed object array in Angular 2 application.

As you can see in Angular 2's official example in plunker, the expected objects are not of Hero type, they are of type Object and the heroes:Hero[] member of HeroListComponent is actually a Object[]. You can see in console that the array is not strongly typed:

http://plnkr.co/edit/Qa22yzPh3JWI8lNZ99Ik?p=preview

I added extra console.log() call to add hero command, you can see in browser console that we don't have a Hero[] but Object[].

So apparently here the conversion does not work:

    this.http.get(this._heroesUrl)
            .map(res => <Hero[]> res.json().data)
            .catch(this.handleError);

and we get Object[] instead of Hero[].

Any ideas about how to map json to correctly typed objects?

More info: https://angular.io/docs/ts/latest/guide/server-communication.html

Beri Bener
  • 93
  • 1
  • 5
  • 3
    is not a conversion. It's a [type assertion](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#4.16). To me, doing this type assertion is a bad idea. What you get from .json() is indeed not an array of Hero instances. It doesn't matter much here, because Hero just has fields. But if it had methods, for example, calling them wouldn't work. Hero should be an interface, not a class, IMO. – JB Nizet Apr 01 '16 at 21:05
  • Thank you for your answer! Yes I observed that methods do not work as you said and started this thread:) If you say it should be an interface.. Do you know of any good example for this? Best way for having model objects with public methods and populating them with json coming from web api? I mean, isn't there any automatic mapper? For nested objects it is really burden to do manual mapping. I just could not find any good example for this basic case interestingly... To populate my objects instantiated from typescript classes with json data coming from web api. – Beri Bener Apr 01 '16 at 23:26
  • Or should I pass the interface to another object's constructor which will initialize itself and expose public methods I need to be invoked from templates? Maybe my approach is wrong here... Thanks! – Beri Bener Apr 01 '16 at 23:30

2 Answers2

3

Your browser is running JavaScript, probably ES5, not TypeScript. In TypeScript the array may be given a type definition but in JavaScript an array is an array of objects. You can put anything in there and it is not typed.

In ES6 there are typed arrays, but they are not actually arrays but rather objects to handle binary data.

The TypeScript notation (type assertion) is only for the compiler to check that the object array is used as if it only contained Hero objects, or compatible ones, and nothing more. It is not enforced in runtime and doesn't emit any code.

Sami Kuhmonen
  • 30,146
  • 9
  • 61
  • 74
  • Thank you for the reply. So if I add a custom function to Hero class (like getNameSpecial()) and call it from template, currently it does not work if that Hero is mapped from web service result... (because it is not Hero actually it does not have any methods, it is an Object as you said) But isn't that TypeScript's duty to add correct methods to prototype and behave like there is a Hero typed class with defined member functions added to its prototype? I still expect to see the methods defined in Hero typescript class in the object mapped from json... What do you think? Thanks!!! – Beri Bener Apr 01 '16 at 21:16
  • 1
    @BeriBener No, TypeScript doesn't do anything magical like that. It would be Angular's responsibility to handle that. The type assertion only says that what comes in is supposed to be valid `Hero` type objects. It might not be and there will be no error messages due to that. Only when the objects are used and don't have the fields one would expect. – Sami Kuhmonen Apr 01 '16 at 21:25
3

Angular 2 observable doesn't 'map' to model

As seen in the duplicate issue in above link, auto-mapping to your own typescript classes from json objects is still a manual process....

I really thought Typescript would be clever enough to transpile in a way such that I'd get the Hero object at the end of map() call.

There should be a easier way to get Hero[] at the end of such a call: .map(res => res.json().data)

Community
  • 1
  • 1
Beri Bener
  • 93
  • 1
  • 5