10

Before angular's new HttpClient was introduced, our objects returned from the http api call could be validated with the instanceof keyword. they no longer can with the HttpClient Module. I'm trying some simple methods but the type checks return false every time. the desired behavior of:

```

getCow() {
    return this.http.get<Cow>(ApiRoute.GET_COW, options)
        .map(res => res as Cow)
        .toPromise()
        .then((c: Cow) => {
            console.log(c instanceof Cow); //this is false
        })
}

```

would return true. does anyone know of a simple way to new up an instance behind the scenes of the http client?

Scott Clark
  • 608
  • 6
  • 21
  • What exactly is Cow? You need to construct it with `new` and initialize with `cow` props. It's not something that Angular or TS can handle for you. – Estus Flask May 21 '18 at 16:31
  • 1
    The above is just a made up example. For sake of the question `Cow` is a class with no properties and the default constructor. – Scott Clark May 21 '18 at 17:36
  • What does `res` contain then? Does this mean that it can be discarded? – Estus Flask May 21 '18 at 18:28
  • How did you create instances of the Cow class before? You should be able to do it the same way. – David May 21 '18 at 18:53
  • @estus bad example I suppose. Lets say `Cow` is a class with one property, `public sound: string;` and res is an object with one property, a string called sound equal to 'moo'. I guess in the end I'm asking why duck typing fails me here after saying its a cow. After all it looks and sounds like a cow. – Scott Clark May 22 '18 at 13:44
  • Duck typing won't fail you if you do duck typing. See this example https://stackoverflow.com/a/50378742/3731501 , as it was mentioned there you can check if all Point2DDefinition props exist in an object and object values are of same type as Point2DDefinition values. This would result in duck typing at runtime+. But you don't do duck typing. `instanceof` does a specific thing, it checks if object prototype chain descends from `Cow`, basically `cow.__proto__ === Cow.prototype`. It's an opposite of duck typing (cow typing?) – Estus Flask May 22 '18 at 14:19
  • 1
    I don't consider any of the solutions on StackOverflow to be a comprehensive solution to the problem. So, I created an npm package angular-http-deserializer for this: https://www.npmjs.com/package/angular-http-deserializer#usage – Jeff Fischer Dec 20 '18 at 16:53

1 Answers1

10

TypeScript uses structural typing, i.e. c object doesn't have to be an instance of Cow class to conform to Cow type.

TypeScript types exist only at compilation time and don't affect JS output in any way (with the exception of emitted types which are used for Angular DI). as Cow asserts that res conforms to Cow type, while instanceof Cow expects that c is an instance of Cow class. Since Cow wasn't instantiated, cow instanceof Cow is false.

A class should be designed to support hydration (possibly via constructor parameters) and be instantiated explicitly:

class Cow {
  sound: string;
}

return this.http.get<Cow>(ApiRoute.GET_COW, options)
    .map(res => Object.assign(new Cow(), res as Cow))
    .toPromise()
    .then((c: Cow) => {
        console.log(c instanceof Cow);
    })

If some logic is needed to construct Cow instance from plain object (validation, nested object construction), this can be done in class constructor or separate helper function (e.g. Cow static method).

Estus Flask
  • 206,104
  • 70
  • 425
  • 565