Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
598 views
in Technique[技术] by (71.8m points)

angular - HttpClient type safety seems to ignore interface

TL;DR:

  1. An angular interface was defined and bound to HttpClient.get() responses.
  2. Responses were mapped to a seemingly generic object type.
  3. Attributes that weren't defined on the interface like id and title were accessible on the response, as well as from .html called (eg. <p>Title is: {{message?.title}}</p>.
  4. If an interface is a contract, shouldn't it be preventing some of this?

New to Angular 4 from a Java background and stumbling on HttpClient type safety. As this guide points out, one can pass an interface to http.get<> to typecheck an HTTP response.

Consider a basic interface with a single foo field:

export interface FooInterface{
   foo: string;
}

Following the aforementioned guide, this interface can easily be bound to a response:

export class RestComponent {

   message: FooInterface;

    http.get<FooInterface>('/api/items').subscribe(data => {
        this.message = data;
        console.log(this.message);
        console.log(typeof(this.message));
        console.log(this.message.foo);
    });

At this point, I hit a random API which does not have a foo field. The actual API has two fields: id and title. To my surprise, when I hit this API, the data object was still created:

enter image description here

Data is a generic object with fields id and title. A call to data.foo return undefined.

Furthermore, while the interface prevents me from accessing these id and title fields directly, they are still available through calls like data['id']. They can also be referenced in the HTML.

<h1>
  id: {{message.id}}
  <br>
  title: {{message?.title}}
</h1>

And this gets rendered! In fact, the only thing the interface seems to do is prevent me from doing something like:

`this.message.title`

That's cool I guess... but if these fields can be accessed via this.message['title'] or reference in html files, then what's the point of the interface?

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

When comming from a Java background, the generic problem with TypeScript typing is that types are not enforced. Whenever you do a cast in TypeScript, no runtime exception is thrown if the object is not of that class. The exception is thrown later when accessing the missing fields of that object.

What you want to do is not compile time type checking (as TypeScript does) but you want to enforce a specific type of the response.

You can do this by checking the response manually:

    http.get<FooInterface>('/api/items').subscribe(data => {
        if (typeof data !== 'object')
          throw new Error('Expected an object')
        if (typeof data.foo !== 'string')
          throw new Error('Expected a string property')
        this.message = data;
    });

If you want to automate this, have a look at JSON Schema validation. You need to define a JSON schema in addition to your TypeScript class. There is a library that will help you keep them in sync. There are also libraries that can automatically generate them from e.g. Spring REST endpoints, so you can keep them in sync with your backend services.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...