1

For example, I have a class:

export class SomeClass {
 id: number;
 name: string;
}

I receive JSON from server than looks like this

[{"Id":1,"Name":"typicalname"},{"Id":2,"Name":"somename"},{"Id":3,"Name":"blablabla"},{"Id":4,"Name":"lol"},{"Id":5,"Name":"lil"},{"Id":6,"Name":"lal"}]

How do I cast a JSON object to a typescript class when properties doesn't match? That's how I do it wright now, and it's not working.

getSomeClass() {
  return this.http.get(this.someClassUrl)
    .map(response => <SomeClass[]>response.json())
    .catch(this.handleError);
}
6
  • potential duplicate of http://stackoverflow.com/q/22875636/1160236 Commented Mar 2, 2017 at 6:19
  • Possible duplicate of How do I cast a JSON object to a typescript class Commented Mar 2, 2017 at 6:21
  • 1
    @HirenParekh I saw that question, but I wasn't found what to do, when properties doesn't match. Commented Mar 2, 2017 at 6:26
  • Using a class is a very bad practice here since instanceof will fail at runtime is anyone tries to use it (hint use interface not class). You need to manually write a function that verifies the shape of the deserialized object in order to validate it. Commented Mar 2, 2017 at 6:29
  • Also, what is handleError? If it is a method on the service that uses this, passing it as shown will fail. If it does not use this why is it a member of the class? Commented Mar 2, 2017 at 6:33

2 Answers 2

1

try this:

getSomeClass() {
  return this.http.get(this.someClassUrl)
    .map(response => {
        let json = response.json();
        return json.map(m => {
            return {
                id: json.Id,
                name: json.Name
            }
        }
    })
    .catch(this.handleError);
}
Sign up to request clarification or add additional context in comments.

3 Comments

This does not do what is asked for. It does not validate the deserialized object.
@AluanHaddad actually, the OP explicitly mentioned that the "properties don't match"; so the question is not about validation.
That is not how I interpreted it, but at any rate the function should modify the OP by not returning something declared as a class since that is misleading given the implementation.
1

When you have a type T and a value x and you write <T>x you are not performing a cast in a runtime sense. You are performing a type assertion. What this means is that you are telling TypeScript that the type of x is T.

In this particular case, if response.json() returns a value typed as any, which is not unreasonable for a deserialization operation, then <T>response.json() will be accepted by the TypeScript compiler for any T. This is because the type any is compatible with (technically assignable to) everything.

However in this case you want to verify the shape of the response and the compiler cannot do this for you. You need to write a validation algorithm that is appropriate.

What is appropriate will depend on the domain of your application, and may be non-trivial, but here is an example. Unfortunately, since your question implies Angular 2 and RxJS, even a simple applicable answer contains a fair amount of incidental complexity.

import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';

function injected(_) {} // emit decorator metadata with flag (not pertinent)

@injected export class SomeService {

  constructor(readonly http: Http) {}

  getSomeValue(): Observable<Expected> {
   return this.http.get(this.someResourceUrl)
      .catch(handleError)
      .mergeMap(response => {
        const deserialized = response.json();
        if (isExpected(deserialized)) {
          // note the type of derserialized is Expected in this block
          return Observable.of(deserialized);
        }
        return Observable.throw('response data did not have the expected shape');
      });
  }
}

export interface Expected {
  id: number;
  name: string;
}

function isExpected(deserialized : any): deserialized is Expected {
  return typeof deserialized.id === 'number' && typeof deserialized.name === 'string';
}

function handleError(error) { // this is not really necessary, but was in the question
  console.error(error); // log
  return Observable.throw(error); // rethrow.
}

The most significant thing here is the isExpected function.

It takes a value of any type, validates it based on our criteria, and states that if it returns true, then the given value was indeed of the expected type, Expected.

What does it mean to be of the expected type?

Well our isExpected function determines that, and provides this information to the TypeScript language by way of its return type which says that if the function returns true, then the value passed to it is of type Expected.

This is known as a User Defined Type Guard function and you can read more about it at https://www.typescriptlang.org/docs/handbook/advanced-types.html.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.