Skip to content

Http ResponseType cannot be set #18586

Open

Description

@zaiddabaeen

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Response type cannot be set for HttpClient methods.

        const options = {headers: headers, params: params, responseType: 'text'};

        return this.http.get(url, options).share();

Would show an error

  Types of property 'responseType' are incompatible.
    Type 'string' is not assignable to type '"json"'.

Expected behavior

It is expected that the response type should be exported like

export type ResponseType = 'arraybuffer' | 'blob' | 'json' | 'text';;

And one would be able to set it using this type. Otherwise the type cannot be changed.

Environment

Angular version: 4.1.1 and still there in 5.0.0-beta.2
as seen here: https://github.com/angular/angular/blob/master/packages/common/http/src/client.ts

Activity

  1. zaiddabaeen commented on Aug 8, 2017

    @zaiddabaeen
    Author

    A workaround:

    const options: {
                headers?: HttpHeaders,
                observe?: 'body',
                params?: HttpParams,
                reportProgress?: boolean,
                responseType: 'text',
                withCredentials?: boolean
            } = {
                headers: headers,
                params: params,
                responseType: 'text'
            };
    
  2. alxhub commented on Aug 9, 2017

    @alxhub
    Member

    @zaiddabaeen currently, this is by design. 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.

    Another workaround is:

    const options = {headers, params, responseType: 'text' as 'text'};
    return this.http.get(url, options).share();
  3. zaiddabaeen commented on Aug 10, 2017

    @zaiddabaeen
    Author

    I understand, but I believe that's unintuitive and confusing to the developers. I don't recall I ever casted a string to a 'string' before. Enumerating and using types as suggested would sound to me as a cleaner solution.

  4. alxhub commented on Aug 17, 2017

    @alxhub
    Member

    @zaiddabaeen the problem is for:

    const res = this.http.get(url, options);

    What is the type of res? It depends on the values in options - but Typescript has no way to know what those values are if it's not inlined.

    In other words:

    const res = this.http.get(url, {responseType: 'text'});

    is not equivalent to

    const options = {responseType: 'text'};
    const res = this.http.get(url, options);

    In the first one Typescript can infer that the type of res is Observable<string>, in the second one it cannot be determined via type inference. If we added this feature, we would have to return an Observable<any> which would be a poor experience.

    I expect most cases where this is desired can be solved with the spread operator:

    // Some options we want to control dynamically.
    const options = {headers: ..., params: ...};
    const res = this.http.get(url, {...options, responseType: 'text'});

    This way Typescript can infer the return type based on the signature and the value of responseType, but options can also be passed in without reconstructing the whole object.

  5. chrillewoodz commented on Sep 6, 2017

    @chrillewoodz

    So instead we have to do workarounds to get the desired effect? That can't be the way to go about this. I've been screaming at my computer for quite a while over this now, I've got a service which wraps the HttpClient but trying to set responseType doesn't work, the only way that I can get the error to go away is by doing responseType: 'text' as 'json'.. None of the workarounds above works.

  6. zaiddabaeen commented on Sep 6, 2017

    @zaiddabaeen
    Author

    @chrillewoodz The default is JSON however. Why are you casting it to json?
    My approach works, and I can confirm that it is now run on production.

  7. chrillewoodz commented on Sep 6, 2017

    @chrillewoodz

    @zaiddabaeen I'm not casting anything to json (as far as I'm aware). This is what I have:

      public get<T>(url: string, params?: {[key: string]: any}, headers?: HttpHeaders): Observable<T> {
        return this.http.get<T>(this.config.host + url, this.getRequestOptions(params, headers));
      }
    
      private getRequestOptions(params?: any, customHeaders?: HttpHeaders) {
    
        let defaultHeaders: HttpHeaders = new HttpHeaders();
    
        defaultHeaders = defaultHeaders.set('Content-Type', 'application/json');
    
        return {
          headers: customHeaders || defaultHeaders,
          params: params ? this.convertJSONtoParams(params) : null
        };
      }
    

    Attempting to add responseType: 'text' to the return of getRequestOptions is what throws the error.

  8. zaiddabaeen commented on Sep 6, 2017

    @zaiddabaeen
    Author

    Use responseType: 'text' as 'text'

  9. chrillewoodz commented on Sep 6, 2017

    @chrillewoodz

    None of the workarounds above works.

    I have already tried all of the above :|

  10. roddy commented on Sep 20, 2017

    @roddy

    I also had this problem, but by removing the <T> from the get call I was able to get it to work using responseType: 'text'as 'text'.

    Eg this does not work and returns the error:

    const options: { responseType: 'text' as 'text', withCredentials: true };
    this.httpClient.get<string>(url, options)
    

    But this does work:

    const options: { responseType: 'text' as 'text', withCredentials: true };
    this.httpClient.get(url, options)
    
  11. yusijs commented on Sep 22, 2017

    @yusijs

    The only way I got it to work without error was using @roddy's example, and that's with inlined options...

    This does not work:
    image

    and neither will:
    image

    Angular v4.4.3

  12. reppners commented on Sep 27, 2017

    @reppners

    The generic MUST not be used when responseType is specified to something other than json because typeof T will then be inferred automatically.

    Take a look at how it is defined for the get() method

        /**
         * Construct a GET request which interprets the body as an `ArrayBuffer` and returns it.
         *
         * @return an `Observable` of the body as an `ArrayBuffer`.
         */
        get(url: string, options: {
            headers?: HttpHeaders;
            observe?: 'body';
            params?: HttpParams;
            reportProgress?: boolean;
            responseType: 'arraybuffer';
            withCredentials?: boolean;
        }): Observable<ArrayBuffer>;
        /**
         * Construct a GET request which interprets the body as a `Blob` and returns it.
         *
         * @return an `Observable` of the body as a `Blob`.
         */
        get(url: string, options: {
            headers?: HttpHeaders;
            observe?: 'body';
            params?: HttpParams;
            reportProgress?: boolean;
            responseType: 'blob';
            withCredentials?: boolean;
        }): Observable<Blob>;
        /**
         * Construct a GET request which interprets the body as text and returns it.
         *
         * @return an `Observable` of the body as a `string`.
         */
        get(url: string, options: {
            headers?: HttpHeaders;
            observe?: 'body';
            params?: HttpParams;
            reportProgress?: boolean;
            responseType: 'text';
            withCredentials?: boolean;
        }): Observable<string>;
        /**
         * Construct a GET request which interprets the body as JSON and returns it.
         *
         * @return an `Observable` of the body as an `Object`.
         */
        get(url: string, options?: {
            headers?: HttpHeaders;
            observe?: 'body';
            params?: HttpParams;
            reportProgress?: boolean;
            responseType?: 'json';
            withCredentials?: boolean;
        }): Observable<Object>;
        /**
         * Construct a GET request which interprets the body as JSON and returns it.
         *
         * @return an `Observable` of the body as type `T`.
         */
        get<T>(url: string, options?: {
            headers?: HttpHeaders;
            observe?: 'body';
            params?: HttpParams;
            reportProgress?: boolean;
            responseType?: 'json';
            withCredentials?: boolean;
        }): Observable<T>;

    This makes perfect sense since the type can only be something angular does not know statically when the responseType is json - all other cases there is no need for a generic.

  13. a-kolybelnikov commented on Oct 10, 2017

    @a-kolybelnikov

    @reppners Hi Stefan, thank you for the explanation above. However, no matter how I try I cannot make the new HttpClient to work in a service that I am using to intercept image urls in my app:

    get(url: string): Observable<any> {
        return new Observable((observer: Subscriber<any>) => {
            let objectUrl: string = null;
              this.http
                .get(url, {headers: this.getHeaders(), responseType: ResponseContentType.Blob} )
                .subscribe(m => {
                  objectUrl = URL.createObjectURL(m.blob());
                  observer.next(objectUrl);
                });
    
            return () => {
              if (objectUrl) {
                URL.revokeObjectURL(objectUrl);
                objectUrl = null;
              }
            }
        });
      }
      getHeaders(): Headers {
        let headers = new Headers();
    
        let token = this.authService.getToken();
        if (token) {
          headers.set('Authorization', 'Bearer ' + token);
        }
        
        return headers;
      }
    

    Neither responseType: 'blob' set directly in the GET request, nor setting it in the options: { } as mentioned above works.

  14. btory commented on Oct 13, 2017

    @btory

    @a-kolybelnikov You can use something like this in your case:

    .get(url, { headers: this.getHeaders(), responseType: ResponseContentType.Blob as 'blob' })

  15. 60 remaining items

  16. sanioooook commented on Nov 10, 2020

    @sanioooook

    One of the solutions that helped me get rid of the compiler error(Type '"text"' is not assignable to type '"json"'.) was creating an HttpRequest using the constructor. And using http.request. I hope someone will help

  17. Lonli-Lokli commented on Feb 6, 2021

    @Lonli-Lokli

    @alxhub does it make sense to reiterate this issue with Typescript 4.1?

  18. evil-shrike commented on Mar 25, 2021

    @evil-shrike

    In 2021 the API is still very weird:

    Works:

    return this.http.post<HttpResponse<Blob>>(this.getUrl(url), {
          responseType: 'blob',
          observe: 'response',
          headers: headers,
          params: params
        }).
    

    Does not work (no matches):

    return this.http.get<HttpResponse<Blob>>(this.getUrl(url), {
          responseType: 'blob',
          observe: 'response',
          headers: headers,
          params: params
        }).
    

    have to use responseType: 'blob' as 'json',

  19. added
    feature: under considerationFeature request for which voting has completed and the request is now under consideration
    on Jun 4, 2021
  20. sk4xd commented on Jan 26, 2022

    @sk4xd

    Cast the httpOptions variable to type Object:

    const httpOptions: Object = {
      responseType: 'blob'
    };
    

    as specified in the docs:https://angular.io/api/common/http/HttpClient#get

    This actually helped me.

  21. noorsaifi commented on Feb 19, 2022

    @noorsaifi

    Worked for me as suggested by @sk4xd .
    download(filters: any) {
    const httpOptions: Object = {
    responseType: 'blob'
    };
    return this.httpClient.post<HttpResponse<Blob>>('/download', filters, httpOptions)
    }

  22. simonthum commented on Mar 31, 2022

    @simonthum

    Just my 2 cents:

    Please make this a documentation ticket. Everybody is already confused. I just discussed this issue with colleagues.

    get() has 15 overloads, 3 of which are generic.
    request() has 17 overloads, 4 of which are generic.

    I am yet to see a compiler that generates useful error messages in face of so many overloads.

    The issue is that both responseType and observe are often required for proper resolution. This is hinted at in the docs of request(), but not get() or any other HTTP method. But even in request() docs are not really explicit about the requirement to select the right overload. Javascript devs typically do not pay much attention to overloading, and that's a big part of the user base.

    Maybe I'm misreading this, but in here the observe is optional. But in many cases typescript needs the information for overload resolution.

    I think the design intends to keep people on the success path in terms of useful types. I wouldn't "fix" that.

    Just make it abundantly clear that the caller has to select the right overload, how to do so, and what to avoid.

  23. gustavostuff commented on Mar 1, 2023

    @gustavostuff

    I tried the workaround mentioned almost at the top (from @alxhub ) but didn't work.

    This is a very simple example.

    get-example.png

    EDIT: We're talking about this headers, right? If so, maybe my params object is not the right type.

    EDIT 2: This is what the editor tells me about those 2:

    EDIT 3: SO never mind, I think I was confused about usage. This worked:

    return this.http.get(url, {
      withCredentials: false,
      responseType: 'text'
    });
  24. Lonli-Lokli commented on Mar 1, 2023

    @Lonli-Lokli

    @alxhub I think that with Typescript 5.0 this issue can be finally resolved
    https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-rc/#const-type-parameters

  25. l-Legacy-l commented on Jul 5, 2023

    @l-Legacy-l

    I was getting the same issue using a body and only this method and @rds-rafael one was working for me

    Mine is:
    const httpOptions: Object = { responseType: 'text' };
    return this.http.post<string>(this.backendUrl + '/url', body, { params, ...httpOptions })

  26. Lonli-Lokli commented on Jul 5, 2023

    @Lonli-Lokli

    @l-Legacy-l
    Use could remove string type from this.http.post<string>(...) call to make it work.
    See this example.

    For everyone - You should just carefully select the proper overload for your needs.

  27. JeanMeche commented on May 15, 2024

    @JeanMeche
    Member

    Since TS 3.4 it is possible to use a const assertion.

        const options = {headers: headers, params: params, responseType: 'text'} as const; 
        return this.http.get(url, options) //  Observable<string>
    
  28. gustavoghcmelo commented on Feb 4, 2025

    @gustavoghcmelo

    @sk4xd thanks. It works for me.

    Cast the httpOptions variable to type Object:

    const httpOptions: Object = {
      responseType: 'blob'
    };
    

    as specified in the docs:https://angular.io/api/common/http/HttpClient#get

    This actually helped me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: common/httpIssues related to HTTP and HTTP ClientfeatureLabel used to distinguish feature request from other issuesfeature: under considerationFeature request for which voting has completed and the request is now under consideration

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      Sponsor
      SponsoredKunjungi sekarang
      Promo