Angular 4/5 HttpClient: Argument of type string is not assignable to 'body'
Asked Answered
B

4

28

The Angular docs say:

The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions, and inspecting those can be necessary. To do this, you can tell HttpClient you want the full response instead of just the body with the observe option:

http
  .get<MyJsonData>('/data.json', {observe: 'response'})
  .subscribe(resp => {
    // Here, resp is of type HttpResponse<MyJsonData>.
    // You can inspect its headers:
    console.log(resp.headers.get('X-Custom-Header'));
    // And access the body directly, which is typed as MyJsonData as requested.
    console.log(resp.body.someField);
  });

But when I try that, I get a compilation time error (no runtime errors though, works as expected):

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

Why? I use "@angular/http": "^5.1.0"

Here is my version of the code:

  login(credentials: Credentials): Observable<any> {
    const options = {
      headers: new HttpHeaders({'Content-Type': 'application/json'}),
      observe: 'response'
    };
    return this.httpClient.post<any>(`${environment.USER_SERVICE_BASE_URL}`,
      {'username': credentials.username, 'password': credentials.password}, options)
      .map((res) => ...
Bought answered 11/12, 2017 at 20:55 Comment(3)
...I get a compilation time error... <= You should include your code. The code above is a copy/paste from the angular web site. (I am not the downvoter but my guess as to why you were downvoted would be that guessing why code fails is impossible if that code is not included in the question)Aduwa
@Aduwa I added my version of the code. I get the same error with a get request (without the body-part) and without "headers"-property. so essentially it IS the same code as at the angular websiteBought
You have to inline the options.Aduwa
A
50

You have to inline the options. See github ticket #18586, entry by alxhub on August 9 2017.

Typescript needs to be able to infer the observe and responseType values statically, in order to choose the correct return type for get(). If you pass in an improperly typed options object, it can't infer the right return type.

login(credentials: Credentials): Observable<any> {
    return this.httpClient.post<any>(`${environment.USER_SERVICE_BASE_URL}`,
      {'username': credentials.username, 'password': credentials.password}, {
      headers: new HttpHeaders({'Content-Type': 'application/json'}),
      observe: 'response'
    })
      .map((res) => ...
Aduwa answered 11/12, 2017 at 21:13 Comment(0)
B
37

Typescript complains about this problem

Type 'string' is not assignable to type "body"

To solve this, convert string to body manually. Example:

    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json'
      }),
      observe: 'response' as 'body'
    };
    return this.http.post<any>(url, data, httpOptions);
Bifrost answered 16/1, 2019 at 13:25 Comment(1)
Also works with 'response'. const options = { headers: headers, observe: response as 'response' };Went
P
10

The way I got around this, without inline-ing the options (which can lead to code that's not as clean) was to create an interface for the request options. Code looks like this :

export interface IRequestOptions {
    body?: any;
    headers?: HttpHeaders | { [header: string]: string | Array<string> };
    observe?: any;
    params?: HttpParams | { [param: string]: string | Array<string> };
    reportProgress?: boolean;
    responseType?: "arraybuffer" | "blob" | "json" | "text";
    withCredentials?: boolean;
}

Then this is used as such :

const options: IRequestOptions = {
    headers: new HttpHeaders({"Content-Type": "application/json"}),
    observe: "response"
};
return this.httpClient.post(`${environment.USER_SERVICE_BASE_URL}`,
    {"username": credentials.username, "password": credentials.password}, options)
    .pipe(
        map((res: HttpResponse<any>) => ...
    );

Change for original post to use lettable or pipeable (whatever the current name is today) operators

Propylene answered 23/2, 2018 at 15:55 Comment(0)
R
0
import { HttpHeaders, HttpParams } from '@angular/common/http';
export interface IRequestOptions {
    headers?: HttpHeaders | { [header: string]: string | string[]; };
    observe: "response"; 
    params?: HttpParams | { [param: string]: string | string[]; };
    reportProgress?: boolean; 
    responseType?: "json";
    withCredentials?: boolean; 
}
Rainier answered 7/4, 2020 at 6:50 Comment(1)
Hi Michael, and welcome to SO! Please try to avoid code-only answers and describe your solution a bit.Francenefrances

© 2022 - 2024 — McMap. All rights reserved.