简体   繁体   中英

Angular change Interface definition when I use as with promise

I have predefined properties in interface as below:

export interface User {
  userID : number;
  userName : string;
  userAge : string;
}

Also, I have service which returns some dummy data from some dummy REST api.

getUsers(){
    return this.http.get("https://fakerestapi.azurewebsites.net/api/Authors").toPromise();
  }

and in component I am consuming this service and convert data to userlist as per below code:

 _service.getUsers().then(i => { this.userList = i as User[]; console.log(this.userList) });

as you can see, I used 'AS' to convert response to my respective user[] array.

ISSUE:
dummy REST api retruns following data

{
    "ID": 1,
    "IDBook": 1,
    "FirstName": "First Name 1",
    "LastName": "Last Name 1"
  }

and user class doesn't have property of ID, IDBook etc. still when I check console, it automatically change the definition of User class and shows all the data although properties are not matched.

Link: https://stackblitz.com/edit/angular-pryy3f?file=src%2Fapp%2Fappservice.service.ts

As per my understanding only match properties should be displayed not all.

In typescript as keyword is not a type casting but a type assertion. So what is happening is this:

  • you ask for a User[]
  • you receive a JSON object[]
  • the JSON object[] is transformed into a User's interface array (look at what is an interface in typescript)
  • the compiler accept this.userList as a User[] but it is only a type assertion

So using as keyword is redundant in this case and completely useless.

A type assertion is only useful for autocompletion and other similar things that are elaborated at compile time. But it is not a real cast. So when you execute your code this.userList is always a JSON Object[]

If you need real User objects you should do something like this:

_service.getUsers().then(i => { 
    this.userList = i.map(u => new User(u)); 
    console.log(this.userList) 
});

Obviously you should define a constructor into your User class

EDIT

Looking at your Authors file I can see you are using an xml syntax that is converted into an array treated as Object. For this reason map operator fails. I have changed a bit your code

User

export class User {
  userID : number;
  FirstName : string;
  userAge : string;

  constructor()
  constructor(u: User)
  constructor(u?: User) {
     this.userID = u && u.userID || -1;
     this.FirstName = u && u.FirstName || '';
     this.userAge = u && u.userAge || '';
  }

  clone(): User {
    // TODO CLONE USER
    return new User();
  }
}

AppComponent

import { Component } from '@angular/core';
import { AppserviceService } from './appservice.service';
import { User } from './user';
import 'rxjs/add/operator/map';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private userList: User[] = [];
  constructor(private _service: AppserviceService) {
    _service.getUsers().then(i => {  
      for (let c=0; c<10; c++) {
        let u = new User(i[c]);
        this.userList.push(u);
      }

      console.log('this.userList', this.userList);
    });
  }
}

Defining an interface with Typescript has absolutely no effect on generated Javascript code, since interfaces do not exist in Javascript. You can figure it out by playing with the Typescript playground . Interfaces are just something that help you (as a developer) and your team detecting errors at compilation time.

So the object you return from HTTP will always be as is, regardless of the interface you apply to it, because there is no such thing as interface or cast in Javascript.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM