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

What are the steps for manually integrating Bootstrap into an Angular project?

I'm currently working on an Angular 5 project within a private domain where I am unable to utilize the npm-install command. As a result, I have manually added Bootstrap's CSS and JS files to my project. I am now unsure how to properly link these ...

How come I am unable to fetch classes and enums from a namespace?

When using Typescript with pg-promise, I am facing an issue where I can't import the classes and enums as I normally would. Typically, when working with a library, I import a type, use it, and everything functions properly. However, in the snippet bel ...

Instructions for obtaining the most recent event using the `fromEvent` function

I'm having trouble capturing the final event generated by the keyup event using rxjs. Every time, my console is filled with multiple log messages. Here's the Angular directive I'm working with: import { Directive, AfterContentChecked, Eleme ...

Concealing the Submit Button During Server Processing (Issues with Binding?)

My Angular 2 form is set up to send data to the server asynchronously. I want to provide users with visual feedback during the waiting period by changing the blue 'submit' button to a greyed-out 'Please wait...' button. To achieve this, ...

Generating typescript definitions for Polymer 2.4 packages

According to information provided in the latest announcement, declarations are now automatically generated from the Polymer source. I recently upgraded to Polymer 2.4 and encountered an error during project build due to a missing typescript definition fil ...

Tips on sending various properties to makeStyles() using TypeScript

After learning how to pass 1 prop to makeStyle() from a resource, I decided to try passing in 2 props for my project. However, I encountered an error stating cannot find name 'props'. Any assistance on this issue would be greatly appreciated! con ...

The act of employing `Function.prototype.run` within an Angular TypeScript class is deemed as illegitimate

Is there a way to globally define a new function called run within my Angular component as shown below? Function.prototype.run = function (delay: number) { // some content; }; However, the compiler shows an error that the Property 'run' does n ...

Stop Node Modules from Referencing Global Location

Recently, I ran into an issue after updating my project from Git. The problem arose when trying to use ngx-material-timepicker in conjunction with the luxon library. (It's important to note that ngx-material-timepicker isn't a new addition to th ...

Utilizing Angular 5: Enhancing ngFor with a Pipe and a Click Event

Iterating through an array of objects using *ngFor, I apply various filters via pipes to manipulate the resulting list. One of these pipes relies on a user input from a search field. Upon clicking on one of the ngFor elements, the corresponding object is p ...

Issue with logging messages using console.log in Knex migration script

My concern: I am facing an issue where the console.log('tableNobject: ', tableNobject) does not get logged in my knex migration script. I have attempted the following code snippets: //solution A export async function up(knex: Knex) { const ta ...

converting an angular object into a string representation

I stumbled upon this guide: . and it includes the following piece of code: import { Component } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl ...

Modifying the user interface (UI) through the storage of data in a class variable has proven to be

If I need to update my UI, I can directly pass the data like this: Using HTML Template <li *ngFor="let post of posts; let i = index;"> {{i+1}}) {{post.name}} <button (click)="editCategory(post)" class="btn btn-danger btn-sm">Edit</butto ...

Why is TS1005 triggered for Redux Action Interface with an expectation of '=>'?

I'm finding it difficult to identify what's causing this issue, as shown in the esLint error from Typescript displayed in the screenshot below: https://i.stack.imgur.com/pPZa7.png Below is the complete code, which consists of actions for redux. ...

What is the impact on active subscriptions when the browser is closed or refreshed?

Within the app component (the root component) of an Angular application, I have a few subscriptions set up. Since the app component remains active and is never destroyed until the application is closed, the ngOnDestroy method of the app component does not ...

Creating circular artwork with PixiJS: A step-by-step guide

I am trying to create a circular image with specific height and width dimensions, but have not found a satisfactory solution. Currently, I can achieve this using a texture, however it is drawn multiple times in the same position. const test = new Graphic ...

Angular 2 Return {responseBody: "assigned variable with [object Object]"}

In my Angular 2 application, I am encountering an issue where I am sending a variable from a service to a component. In the template, the variable appears as expected, however, when attempting to send this variable to a PHP script using POST, I receive [ ...

What steps should I take to resolve the issue of my endpoint failing to accept POST requests?

I am in the process of developing a customized API, with an endpoint that is specified as shown below: https://i.stack.imgur.com/sZTI8.png To handle the functionality for this endpoint, I have set up a Profiling Controller. Inside my controller directory ...

Converting milliseconds into days, hours, minutes, and seconds using Angular

Currently, I am attempting to convert milliseconds to the format dd:hh:mm:ss. For example, given 206000 milliseconds. The desired format for this value would be: 00:00:03:26. However, when utilizing the following code: showTimeWithHour(milliSeconds: numb ...

Can Cloud Functions be used to establish a connection between Cloud Firestore and Realtime Database?

My current project involves designing my firebase database with a unique approach. I am looking to establish a connection between certain document fields in firestore and real-time database fields. This way, any changes made in the real-time database will ...

Could one potentially assign number literals to the keys of a tuple as a union?

Imagine having a tuple in TypeScript like this: type MyTuple = [string, number]; Now, the goal is to find the union of all numeric keys for this tuple, such as 0 | 1. This can be achieved using the following code snippet: type MyKeys = Exclude<keyof ...