angular: Http ResponseType cannot be set
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
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 122
- Comments: 71 (8 by maintainers)
Links to this issue
- angular - HTTPClient POST tries to parse a non-JSON response - Stack Overflow
- typescript - Angular 4/5 HttpClient: Argument of type string is not assignable to 'body' - Stack Overflow
- Pass responseType: 'blob' via post request in Angular - Stack Overflow
- javascript - Angular 4: How to read content of text file with HTTPClient - Stack Overflow
- node.js - Angular7: unable to set {responseType: 'text'} - Stack Overflow
- angular5 - Angular 6 - Error using http.get responseType: ResponseContentType.Blob - Stack Overflow
- Setting http option as 'responseType: 'text'' causes compile failure for a http post request with angular HttpClient - Stack Overflow
- PDF Blob is not showing content, Angular 2 - Stack Overflow
- angular5 - how to download a pdf file from an url in angular 5 - Stack Overflow
- Angular 5 - How to display image in HTML that comes as output from a http POST? - Stack Overflow
Commits related to this issue
- Correctiond de ``` Argument of type '{ responseType: "text"; }' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"... — committed to anthony-o/SGDF-Inspirations by anthony-o 6 years ago
- feat(common): improved typing for HttpClient HttpClient uses overloaded signatures in TypeScript to determine return types for HttpClient calls based on two pieces of information: what the response t... — committed to alxhub/angular by alxhub 6 years ago
- http options inline code replaced see https://github.com/angular/angular/issues/18586 — committed to marclabud/behandlungsjournal by deleted user 6 years ago
- DownloaderService: crazy error: https://github.com/angular/angular/issues/18586#issuecomment-330945826 — committed to ducthang310/image-helper-webapp by ducthang310 4 years ago
- client: removed buggy responseType, polish https://github.com/angular/angular/issues/18586 — committed to P1100/Virtual-Labs by P1100 4 years ago
@zaiddabaeen currently, this is by design. Typescript needs to be able to infer the
observe
andresponseType
values statically, in order to choose the correct return type forget()
. If you pass in an improperly typedoptions
object, it can’t infer the right return type.Another workaround is:
Closing, the solution is to pass a correctly typed argument.
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 setresponseType
doesn’t work, the only way that I can get the error to go away is by doingresponseType: 'text' as 'json'
… None of the workarounds above works.@zaiddabaeen the problem is for:
What is the type of
res
? It depends on the values inoptions
- but Typescript has no way to know what those values are if it’s not inlined.In other words:
is not equivalent to
In the first one Typescript can infer that the type of
res
isObservable<string>
, in the second one it cannot be determined via type inference. If we added this feature, we would have to return anObservable<any>
which would be a poor experience.I expect most cases where this is desired can be solved with the spread operator:
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.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.
I also had this problem, but by removing the
<T>
from the get call I was able to get it to work usingresponseType: 'text'as 'text'
.Eg this does not work and returns the error:
But this does work:
A workaround:
Use
responseType: 'text' as 'text'
try this: … , responseType: ‘text’ as ‘json’
The generic MUST not be used when responseType is specified to something other than
json
becausetypeof T
will then be inferred automatically.Take a look at how it is defined for the
get()
methodThis 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.So whoever won the argument to re-factor Http to HttpClient this way is just wrong.
I have already tried all of the above 😐
For blobs:
.get(url, { 'responseType: 'blob' as 'json' })
Cast the httpOptions variable to type Object:
as specified in the docs:https://angular.io/api/common/http/HttpClient#get
@chrillewoodz’s solution worked for me I had something like
this.httpClient.post<string>(url, data, {responseType: 'text' as 'json'});
and this worked as the api I was called was simply returning a string. Of course, removing the generic means that the whole thing needed to be changed to:this.httpClient.post(url, data, {responseType: 'text' as 'text'});
While I find the workaround to this to only be a minor annoyance it was really furustring that it was so difficult to actually find this answer. It’d be nice if this was better documented or I suppose a more intuitive solution be reached.
https://i.stack.imgur.com/jJoG2.jpg
All feels very convoluted to me. I’d rather have different methods to call for different results rather than the inference which is currently being used.
I eventually came up with this and though I could share it:
https://github.com/angular/angular/issues/18586#issuecomment-327440092 gave a good hint at how to implement this.
This solution also work for the
observe
parameter.Unless I missed something, this could be implemented in Angular directly, isn’t ?
You would need to first put the following in the declaration
@angular/common/http/src/client.d.ts
:Then implement it in
angular/packages/common/http/src/client.ts
:Finally the two namespaces should be properly exported all the way back to
@angular/common/http
.@alxhub Facing the same issue with
observe
propertyonly
observe: 'body'
works.'response'
and'events'
does not.@nortain you definitely don’t need either.
will give you an
Observable<string>
without you having to specify<string>
. You don’t need to say'text' as 'text'
, Typescript knows that.The only time you should be passing a type parameter to an
HttpClient
method is withresponseType: 'json'
. All of the others are implicit.I’ve spent the better part of a day on trying to figure out why postman gave the correct response and http.get didn’t. I thought using http:get<string> might work, but it didn’t. Eventually I found this thread, saw that I had to take the <string> out and use responseType: ‘text’ as ‘text’ as roddy suggested back last September. Then I read to the end of the post and found that it is still a problem. I think this should be fixed, or at the very least documented because it’s far from intuitive!
Please mention this in:
https://angular.io/guide/http#requesting-non-json-data
My code:
I don’t get it though - ng serve blows up with:
But it then serves a page and it works fine in the browser… so is this just a typescript issue and it somehow still does get transpiled into javascript & works?
Enum would’ve been nicer though…
Also, I think the reason people are saying
'text' as 'json'
works is in VSCode if you haveget<string>()
like mine above, it draws a red line under it if you have'text' as 'text'
but accepts'text' as 'json'
I just read @alxhub 's comment about
<string>
being implicit in this case, took out<string>
and VSCode acceptedreturn this.http.get(this.apiURLDisplays+'/'+name, { responseType: 'text' as 'text' })
Seeing stuff like
responseType: 'text' as 'text'
feels broken to me. Maybe the whole Httpoptions
object should have been a well-defined interface with explicit enum types.This is not well documented at all ( https://angular.io/api/common/http/HttpClient#post )
@a-kolybelnikov You can use something like this in your case:
.get(url, { headers: this.getHeaders(), responseType: ResponseContentType.Blob as 'blob' })
@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.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
andobserve
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.
I have to hack it with
const options = {responseType: 'text' as 'json'};
this.httpClient.get<string>(url, options).subscribe()
@ghillert unfortunately that would suffer from the same problem. The type of the field in the interface would be the enum type, not the particular value.
HttpClient
’s type inference depends on the type of the object being passed toget()
being narrowed to a specific value type.There’s an easy solution to all this, though:
Basically, you just have to specify the
responseType
andobserve
values directly in the call toget()
. Everything else can go in theoptions
object.this solution works for me
@rgoupil this is a cool idea!
TS is easy))
Same problem occurred over here… Adding ‘text’ as 'text solved it…
Thanks, @alxhub. I re-read your comment and I begin to understand what’s happening (should’ve read that more carefully).
I guess it’s my choice to choose between inline, not inline but casting, or not inline but using spread operator for the things that are type-dependent, is it? Basically, these three options:
Is the
as 'text'
variant (second option) really a “workaround” as stated in this issue? Or is it just the way to do things? Is there a preferred option?@tobihagemann
responseType: 'text' as 'text'
is what you want.'text'
is not'json'
.@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:
Neither
responseType: 'blob'
set directly in the GET request, nor setting it in theoptions: { }
as mentioned above works.Any news on this? I’ve been struggling with the same issue on HttpClient.post’s options…
I had the same problem. After a half an hour of trying to implement some of the suggestions above I wen’t ahead with the following (dirty but…):
adding the responseType: ‘blob’ implicitly changes the result to Observable<Blob> / Obserrvable<HttpResponse<Blob>>
Not typing the httpClient.post or httpClient.get solves it for me
gives the error, but
works
I simply added below and worked well in angular 5. const httpOptions = { headers: new HttpHeaders({ ‘Content-Type’: ‘Application/json; charset=UTF-8’ }), responseType: ‘text’ as ‘text’ }; const body = JSON.stringify(params); return this._http.post(url, body, httpOptions);
ok so here’s what worked for me :
actions.ts :
api.ts :
This was the key answer for me: https://github.com/angular/angular/issues/18586#issuecomment-323216764
You have to inline your options object in the call to
get
(orpost
), for the Typescript compiler not to complain about your return type.The only way this compiles without complaining that res is not an ArrayBuffer is to inline the options like this:
@alxhub Ah thank you, that makes more sense and I realize i hadn’t tried dropping the as ‘text’ when removing the generic. Also knowing that the generic is intended for json responses only helps. I appreciate the follow up.