Using Angular's promise feature can alter the definition of an interface

Within a predefined interface, I have the following properties:

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

In addition, there is a service that retrieves dummy data from a mock REST API.

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

Within my component, I consume this service and transform the data into a list of users using the code below:

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

It's worth noting that I utilize 'AS' to convert the response into my specific user[] array.

CONCERN:
The dummy REST API returns the following data:

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

The User class does not include properties like ID or IDBook. Surprisingly, when checking the console, it automatically adjusts the definition of the User class and displays all the data even if the properties do not match.

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

In my opinion, only matching properties should be displayed, not all of them.

Answer №1

When it comes to typescript, the as keyword serves as a type assertion rather than type casting. Here is what is happening:

  • You request a User[]
  • You receive a JSON object[]
  • The JSON object[] gets transformed into an array of User's interface (refer to what an interface means in TypeScript)
  • The compiler accepts this.userList as a User[], but keep in mind it's just a type assertion

Hence, using the as keyword appears redundant and pointless in this scenario.

A type assertion proves useful for features like autocompletion that are handled during compilation. Nonetheless, it does not equate to an actual cast. Therefore, when you run your code, this.userList will always be a JSON Object[].

If you genuinely require User objects, consider implementing something along these lines:

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

Duly note that you need to set up a constructor within your User class.

EDIT

Upon reviewing your Authors file, I noticed you utilize an XML syntax that converts into an array treated as an Object. Consequently, the map operation encounters issues. I have made some modifications to 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 {
    // TO-DO 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);
    });
  }
}

Answer №2

Creating an interface in Typescript does not impact the resulting Javascript code, as interfaces are not recognized in Javascript. To better understand this concept, try experimenting with the Typescript playground. Interfaces serve as tools for developers to catch errors during compilation.

Therefore, when returning an object from an HTTP request, it will remain unchanged regardless of any interfaces applied to it, since Javascript does not recognize interfaces or type casting.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with locating module 'swiper_angular' during Jest unit testing following upgrade from Swiper 6 to 7

Encountering an issue with my unit tests post upgrading from Swiper 6 to 7. My setup includes Angular 12 and Swiper 7.3.1. The unit tests were functioning properly before the upgrade (Swiper version 6.5.9). Incorporating the SwiperModule in my tests like ...

Encountering an issue when attempting to convert data to JSON in Angular 13, as the content type 'text/plain;charset=UTF-8' is not supported. This problem arises after sending data from

I've been attempting to submit a form using the following method: saveBuildcompany(): void { // @ts-ignore // @ts-ignore console.log(this.group?.value); let data2=this.group.value; let serializedForm = JSON.stringify(data2) ...

There is no property called 'x' in type 'y'

Can anyone explain why TypeScript is telling me this: Property 'dateTime' does not exist on type 'SSRPageProps'.ts(2339) Looking at my code below, I have data-time typed. import React from "react"; import axios from "axi ...

Guide on inputting Vue component in props

<template> <v-dialog width="auto" v-model="isShown" transition="dialog-bottom-transition" > <v-card> <v-card-title v-if="title"> {{ title }}</v-card-title> ...

How can I detect when the Redux state object in my Angular (v5) application changes?

Can someone please explain to me the process of creating a listener, like subscribing to the AppState changing? Here is the basic service I currently have. In my view, there is a dispatch action that increments the counter. After the counter changes valu ...

Combining multiple Observables and storing them in an array using TypeScript

I am working with two observables in my TypeScript code: The first observable is called ob_oj, and the second one is named ob_oj2. To combine these two observables, I use the following code: Observable.concat(ob_oj, ob_oj2).subscribe(res => { this.de ...

Unable to transfer an object into a component due to a subscribe restriction

I have encountered an issue: I am making a post request to save an object in the database. The request takes JSON input with the values of the object to be saved. After saving the object in the database, I need my servlet to return the saved object so that ...

The map function is calling an unresolved function or method named "map"

I'm encountering an error with the map method in my code, even after correctly importing 'rxjs/add/operator/map'. I've followed all the necessary steps and upgraded to rxjs 5.0.1, but the error persists. Do you have any suggestions on h ...

Struggling to access the properties of a Material-UI Button

import * as React from "react"; import { styled } from "@mui/material/styles"; import MuiButton from "@mui/material/Button"; import Slider from "@mui/material/Slider"; interface Props { type: "primary" | ...

Ways to specify the type signature for objects that incorporate a fresh method

My understanding is that in TypeScript, we use new() to structurally type a class constructor. But how do we type an object that includes a new method, for example: const k = { new() { return '123' } } ...

What is the process of substituting types in typescript?

Imagine I have the following: type Person = { name: string hobbies: Array<string> } and then this: const people: Array<Person> = [{name: "rich", age: 28}] How can I add an age AND replace hobbies with a different type (Array< ...

Tips on transferring a child Interface to a parent class

Here is my code snippet: LocationController.ts import {GenericController} from './_genericController'; interface Response { id : number, code: string, name: string, type: string, long: number, lat: number } const fields ...

Ways to extract X-Total-Count from json-server using Angular's http.get function

Currently, I am utilizing json-server on localhost:3000. The setup is running smoothly, and I can fetch data in Angular through http.get. My objective is to access the response header X-Total-Count within the Angular .subscribe method. However, I am unab ...

Creating an RxJS subject in Angular 2: A step-by-step guide

Creating an Observable in my Angular component is as simple as the following code snippet: ... ... import { Observable } from 'rxjs/Observable'; .. ... let observable = new Observable( function subscribe(observer) { observer.next(1); ...

What is the proper way to declare a Type for a JSX attribute in Google AMP that utilizes square brackets?

When utilizing AMP's binding feature, you must apply specific attributes that encapsulate an element's property with square brackets and connect it to an expression. An example from AMP is shown below: <p [text]="'Hello ' + foo"> ...

Using [(ngModel)] on a field within an object that is nested inside another object

I am facing a challenge as I attempt something that may or may not be feasible. Within an Angular form, I have an object structured like this: export class NewUserRegisterModelDTO{ userData:UserDetailModelDTO; roles:RoleModelDTO[]; ownerData:O ...

What is the best approach for resolving this asynchronous task sequencing issue in JavaScript?

Below is a code snippet where tasks are defined as an object and the function definition should ensure the expected output is met. Let tasks = { ‘a’: { job: function(finish){ setTimeout(() => { ...

"Upon the initial page load, the persistence of values in local storage using Next.js, React, and Recoil

Check out this code I have, const Layout: React.FC<LayoutProps> = ({ children }) => { const darkMode = useRecoilValue(darkModeAtom) console.log('darkMode: ', darkMode) return ( <div className={`max-w-6xl mx-au ...

What are some ways to implement Material UI's Chip array to function similar to an Angular Chip Input?

Can the sleek design of Angular Material's Chip input be replicated using a React Material UI Chip array? I am attempting to achieve the modern aesthetic of Angular Material Chip input within React. While the Material UI Chip array appears to be the ...

Angular Form Template Unidirectional Data Binding Example

I'm facing a challenge with one-way binding to a default value in my HTML form. Within my component, I have a Connection string that is initially set from local storage: export class AuthAdminComponent implements OnInit { public authenticated = f ...