25

Using Angular2 and typescript, I have JSON returned from a webApi and I need to get it into an array of a certain type. I can't figure out how to cast the json to the interface I need.

My type is this interface:

export interface Country {
    Id: number;
    Name: string;
}

My mock returns this array:

export var COUNTRIES: Country[] = [
    { "Id": 11, "Name": "US" },
    { "Id": 12, "Name": "England" },
    { "Id": 13, "Name": "Japan" }
];

Here is my code: country.service.ts

@Injectable()
export class CountryService {
    private _http = null;

    constructor(http: Http) {
        this._http = http;
    }

    getCountries() {
        return Promise.resolve(COUNTRIES);
    }
}

Then I call it with this:

export class CountryPickerComponent {
    public countries: Country[];

    constructor(private _countryService: CountryService) { }


    getCountries() {
        this._countryService.getCountries().then(data => this.setData(data));
    }

    setData(data) {
        //this.countries = data;
        this.countries = JSON.parse(data);
        var q = 1;
    }

    ngOnInit() {
        this.getCountries();
    }
}  

As you can see, I have a class variable called countries that is an array of the Country interface. This works as expected. (I know I don't need that setData method - it's for debugging)

Next, I changed the service to a wepApi call which returns this json.

"[{"name":"England","id":1},{"name":"France","id":2}]"

I changed the getCountries method in the service to:

getCountries() {
    return new Promise(resolve=>
        this._http.get('http://localhost:63651/Api/membercountry')
            .subscribe(data => resolve(data.json()))
    );
}

Using JSON.parse, I convert this to an array which I can then use in angular2. It works. It creates the array with the data. But it's not an array implementing the Country interface.

Notice that the field names from the JSON are all lower case, but the interface properties starts with an uppercase and I coded to the interface. As a result, when I got the real json it does not work. Is there a way to 'cast' this to the interface, which should throw an error and let me know something is wrong?

Many examples use map in the get as shown below:

  getCountries() {
        var retVal;

        return new Promise(resolve=>
            this._http.get('http://localhost:63651/Api/membercountry')
                .map(ref => ref.json())
                .subscribe(data => resolve(data.json()))
        );
    }

I can't find documentation on this. When I try it I never get data but there is no error. No compile error and no runtime error.

1
  • 2
    If you are using angular 2 beta, map is not available in Observable returned from http.get(), instead subscribe is used directly on Observable. Commented Dec 29, 2015 at 18:29

2 Answers 2

15

You can do a type assertion to the interface you are expecting on the object created by JSON.parse.

 this.http.get('http://localhost:4200/').subscribe((value: Response) => {
    let hero = <ServerInfo>value.json();
  });

However this will not result in any errors if the server sends you bad objects because of 2 reasons.

At compile time the transpiler does not know what the server will send.

At runtime all type information is erased since everything gets compiled to javascript.

Sign up to request clarification or add additional context in comments.

2 Comments

At runtime it's untyped, At compile time we don't what the api will return. That makes sense but it does not really help. I guess we need to know the json first and build our object accordingly.
@user1617407 exactly. You can't cast to an unknown interface.. well you can cast to any, but that doesn't really help either.
5

Viktor Savkin has a library to do runtime checking of types for this sort of situation but it doesn't work with interfaces because Typescript doesn't export runtime information on interfaces. There is a discussion about this here.

There are two possible solutions:

  • If you know the data coming from the api is the correct shape, and you are just looking to give your IDE / the compiler information about this shape, you can cast as per toskv's answer.
  • If you are not sure of the api and want your app to throw if the data is the wrong shape, you are going to have to implement your own check. This is a bit of overhead but it's well worth it for larger apps.

In general I see Typescript's types as compiler hints more than a rigorous type system - it has to compile down to an untyped language after all.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.