Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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) => ...
like image 366
Phil Avatar asked Dec 11 '17 20:12

Phil


3 Answers

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) => ...
like image 61
Igor Avatar answered Nov 16 '22 16:11

Igor


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);
like image 36
Imtiaz Shakil Siddique Avatar answered Nov 16 '22 16:11

Imtiaz Shakil Siddique


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

like image 10
blo0p3r Avatar answered Nov 16 '22 16:11

blo0p3r