Deciphering intricate Type Script Type declarations

I am seeking clarification on how to utilize the object type for sending headers, aside from HttpHeaders provided in the HTTPClient declaration.

While working with Angular HttpClient, I aim to include headers using an Object. However, I am unsure of how to define an object of type [header: string]: string | string[];. I need assistance understanding this object declaration, as I am encountering a similar issue with HttpParams. My code snippet is as follows:

getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): Observable<LoggedInUserResponse> {
   return this.http.get<LoggedInUserResponse>(`${environment.apiBaseUrl}/auth/loggedInUser`, { headers: requestHeaderParam }); 
}

The error message displayed in VS Code is as follows:

[ts] Argument of type '{ headers: GetLoggedInUserHeaderRequestParam; }' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'. Types of property 'headers' are incompatible. Type 'GetLoggedInUserHeaderRequestParam' is not assignable to type 'HttpHeaders | { [header: string]: string | string[]; }'. Type 'GetLoggedInUserHeaderRequestParam' is not assignable to type '{ [header: string]: string | string[]; }'. Index signature is missing in type 'GetLoggedInUserHeaderRequestParam'.

The Request Param type is defined as below:

export interface GetLoggedInUserHeaderRequestParam {
  uid: string;
  PSID?: string;
}

The HttpClient Declaration is outlined as below:

HttpClient.get(url: string, options: {
   headers?: HttpHeaders | {
    [header: string]: string | string[];
   };
   observe?: "body";
   params?: HttpParams | {
    [param: string]: string | string[];
   };
   reportProgress?: boolean;
   responseType: "arraybuffer";
   withCredentials?: boolean;
}): Observable<ArrayBuffer>

Your guidance is greatly appreciated!

Note: My query pertains to utilizing the Object directly, specifically in relation to its declaration type { [header: string]: string | string[]; } within the HttpClient context.

Answer №1

There is a discrepancy in the signature of

GetLoggedInUserHeaderRequestParam
and Direct Headers object

For instance

  export interface GetLoggedInUserHeaderRequestParam {
      uid: string;
      PSID?: string;
    }

The interface only allows an object with uid and optionally PSID, whereas the Headers direct object

   {
      [header: string]: string | string[];
   }

States that it can accept an object with any number of keys of type string and values as string or an array of strings.

The key difference is that your interface enforces Typescript to require an object with exact key names, while the header object can accommodate any number of keys with string values like

{
  "uid" : "1234",
  "PSID" : "1223",
  "name" : "test",
  ..... 
}

You can resolve this issue by defining the interface as

interface GetLoggedInUserHeaderRequestParam {
    [name: string] : string | string[];

}

And then calling the HTTP method as

let header: GetLoggedInUserHeaderRequestParam  = {
  "uid" : "1234",
  "PSID" : "1234"
}

getLoggedInUser(header);

Answer №2

it might look something like this:

HTTP headers are in the 'headers' variable, while the data you provide goes into the request body.

getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): Observable<LoggedInUserResponse> {

     let headers = new HttpHeaders();
     headers = headers.set('Content-Type', 'application/json');

   return this.http.get<LoggedInUserResponse>(`${environment.apiBaseUrl}/auth/loggedInUser`, JSON.stringify(requestHeaderParam),
    { headers: headers }); 
}

for parameters:

    import {HttpParams} from "@angular/common/http";

getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): Observable<LoggedInUserResponse> {

    const params = new HttpParams()
        .set('uid', requestHeaderParam.uid)
        .set('PSID', requestHeaderParam.PSID);

     return this.http.get<LoggedInUserResponse>(`${environment.apiBaseUrl}/auth/loggedInUser`, 
        {params:params}); 
}

Answer №3

When attempting to pass an object that follows the structure of

GetLoggedInUserHeaderRequestParam
as either headers or params to HttpClient.get's options, TypeScript intervenes due to safety concerns.

The issue lies in declaring the parameter

requestHeaderParam: GetLoggedInUserHeaderRequestParam
. This declaration specifies that requestHeaderParam:

  1. must include a uid field with type string.
  2. may contain a PSID field with type string.

However, this does not restrict additional fields from being present. An object could meet the interface requirements and still have extra fields, some of which may not be of type string. To illustrate this point, consider the following excerpted code:

interface GetLoggedInUserHeaderRequestParam {
    uid: string;
    PSID?: string;
}

function getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): void {
}

const params = {
    uid: "1",
    rogue: getLoggedInUser, // Passing a function!
};

// The compiler accepts this even though params has an extra field.
getLoggedInUser(params);

// However, this call does not compile.
getLoggedInUser({
    uid: "1",
    rogue: getLoggedInUser,  // Passing a function!
})

I provided two instances of calling getLoggedInUser; the first is successful while the second results in an error. This discrepancy might lead TypeScript users to believe that undefined interface fields are prohibited, but this is not always the case. When applying an interface to an object literal, TypeScript rejects non-defined fields, although this rule only applies to object literals. (A type assertion can override this behavior.) The initial call to getLoggedInUser showcases that objects can fulfill an interface and still feature additional fields.

So why is this problematic? The

{ [header: string]: string | string[] }
definition for headers indicates that HttpClient.get requires an object with string keys and values that are strings or arrays of strings exclusively. There is no room for other types. As shown earlier, there is no guarantee that requestHeaderParam adheres to these specifications, potentially introducing incompatible fields. Consequently, TypeScript raises an error. The same reasoning applies if utilizing params.

The appropriate solution depends on your code's context. A basic approach involves implementing a type assertion:

this.http.get(..., { headers: requestHeaderParam as unknown as Record<string, string>})

An intermediary step through unknown (or any) is necessary due to the mismatched types. Note that solely relying on a type assertion provides no safeguard against passing non-string or non-string[] values. Without validation, issues or undefined behavior may result when invoking HttpClient.get.

Alternatively, you can conduct the type assertion after confirming the absence of extra fields within the object.

Another viable option involves transforming requestHeaderParam into an instance of a specific class that enforces permissible fields and offers a method returning a plain JS object that aligns with headers' criteria.

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

Incorporating Google Pay functionality within Angular applications

I have been attempting to incorporate Google Pay into my Angular project, but I am struggling to find reliable resources. My main issue revolves around the following code... <script async src="https://pay.google.com/gp/p/js/pay.js" onloa ...

Angular - Utilizing nested arrays for efficient file uploading

I am currently working on building a file uploader using Angular. My goal is to be able to upload different types of files, with each button capable of uploading multiple files at once. However, I am running into an issue where the uploaded files are not b ...

Is there a way to replicate this exact toggle selection functionality from Angular Material using just HTML and CSS?

I'm attempting to incorporate this functionality into my Angular project ( link ), but I'm facing challenges with styling it using CSS. Could this be due to limitations in overriding the default settings? I came across this helpful resource: sour ...

The problem with URL encoding causing issues with Angular 2 navigation

I've encountered an issue with my Angular 2 website. When I input optional parameters in Chrome, such as this URL gets converted to and fails to locate the page in Chrome. Strangely, it works perfectly when pasted in incognito mode. As a newcomer to ...

Accessing an external API through a tRPC endpoint (tRPC Promise is resolved before processing is complete)

I want to utilize OpenAI's API within my Next.js + tRPC application. It appears that my front-end is proceeding without waiting for the back-end to finish the API request, resulting in an error due to the response still being undefined. Below is my e ...

Issue with a child element that is malfunctioning because of the parent element

There seems to be an issue with the child tag link not working because of the parent tag link. Is there a solution for this problem? Please provide suggestions. Here is the code snippet: <div class="d-flex flex-wrap mx-auto mb-4"> < ...

Transform an Array<string> into an object with the strings of the array as keys

I am attempting to develop a type conversion method that transforms an array of strings into an object with the string values as keys. type ObjectFromKeys<T extends Array<string>> = { [K in keyof T]: string } declare const o: ObjectFromKeys& ...

What's the best way to show the calendar date in the mm-dd-yyyy format within an input search tag in Angular

I have implemented the <input class="input-group1 search" id="to" placeholder="dd-mm-yyyy" [(ngModel)]="toDate" value="" name="dp3" type="date"> in my code, which currently di ...

Unlocking the potential: passing designated text values with Javascript

In my current React code, I am retrieving the value from cookies like this: initialTrafficSource: Cookies.get("initialTrafficSource") || null, Mapping for API const body = {Source: formValue.initialTrafficSource} Desired Output: utmcsr=(direct)|utmcmd=(n ...

How to pull data from an HTTP source without relying on promises

Here is a simplified version of the service code I am using: Load(Channel: any) { // let URL = Globals.AppSiteRoot + Channel.URL return this.LoadData(URL) } Load_Default() { let URL = Globals.AppSiteRoot + "di ...

Reorganizing the layout of a React grid in response to changes in screen size

Currently, I'm facing a challenge in creating a container for a React app that includes a Highcharts chart and three textboxes. My struggle lies in getting the three textboxes to adjust and rearrange beneath the chart component when resizing the scree ...

Tips for replacing HTTP response in Angular 4

One of my challenges involves using a specialized HttpService that inherits from Angular's native Http: export class HttpService extends Http { } I am trying to figure out how to intercept/override the response: My attempt to do this looks like: r ...

Incorporating PrimeNG into your Bootstrap 4 projects

Exploring various UI libraries for a new Angular 2 project has been an interesting journey. From Ng-Bootstrap and Material to PrimeNG, each library has its own strengths and weaknesses. Currently, I find that PrimeNG offers a wider range of components comp ...

Transferring the web application context to an Angular2 service

In my project, I am attempting to pass a variable named _contextPath from a Javascript evaluated variable in a JSP file located in the folder structure shown below. The goal is to make this _contextPath variable available in a Typescript Service called job ...

What is the best way to retrieve the final entry from a JSON file while using json server with Angular?

I'm currently working with a JSON file where I am making post requests followed by get requests. My goal is to retrieve the latest record in each get request after submitting a post request. For example: [ { "id": 1, "title&qu ...

Ways to turn off Typescript alerts for return statements

I'm looking to turn off this Typescript warning, as I'm developing scripts that might include return values outside of a function body: https://i.stack.imgur.com/beEyl.png For a better example, check out my github gist The compiled script will ...

Validator in Angular FormControl ensures that two fields have the same value or both are empty

When filling out a form with four fields, I have encountered a specific requirement. Two of the fields are mandatory, which is straightforward. However, the other two must either both be empty or both have a value - essentially resembling an XNOR logic sta ...

Troubleshooting problem with Angular HttpClient when invoking Bing Maps Locations REST APIs

Currently, I have successfully implemented a Bing Maps call using Angular 4 Http service: this.http.get("{absolute URL of Bing Maps REST Locations, with options and key}") Now, I am trying to transition this call to use the newer HttpClient service in An ...

Angular4 encountered an error: $(...).DataTable is not recognized as a function

I utilized the bootstrap4 datatable jQuery function within my Angular4 application to construct a sortable table. Below is the code snippet. .component.ts import { Component } from '@angular/core'; declare var $: any; @Component({ template ...

Modify the arrow design for the expansion panel's default arrow style

Looking to customize the design of an angular expansion panel? Check out the images below for inspiration: Before Customization: After Customization (Not expanded): After Customization (Expanded): Wondering how to achieve this look using angular materi ...