For an operation whose response is a file (isResponseFile), the typescript-fetch runtime returns the body through BlobApiResponse.value():
export class BlobApiResponse {
constructor(public raw: Response) {}
async value(): Promise<Blob> {
return await this.raw.blob();
};
}
The name the server advertises in Content-Disposition (attachment; filename="invoice-2026-09.pdf") is dropped. Any client that wants to save the download under its real name has to bypass the generated method, call the xxxRaw() variant, read raw.headers and parse the header itself, and every project ends up with its own copy of that parsing.
Every other runtime in this repository already does it for the user:
typescript generator: ResponseContext.getBodyAsFile() returns new File([data], fileName, { type }) with fileName read from content-disposition (typescript/http/http.mustache).
- Java (
okhttp-gson, jersey2/3, apache-httpclient, feign, native, vertx): prepareDownloadFile names the temp file from Content-Disposition.
- Python (
api_client.mustache, __deserialize_file): idem.
- C# (
ClientUtils, FileParameter): idem.
typescript-fetch is the exception.
Describe the solution you'd like
Keep BlobApiResponse, make it return a File named after Content-Disposition (empty name when the header is absent, type taken from the blob):
async value(): Promise<File> {
const blob = await this.raw.blob();
return new File([blob], parseContentDispositionFilename(this.raw.headers) ?? '', { type: blob.type });
}
plus an exported parseContentDispositionFilename(headers) handling the RFC 5987 filename*=UTF-8''... form first and the plain filename= form otherwise.
Backward compatible: File extends Blob, so the generated methods still satisfy their declared Promise<Blob> return type and existing callers keep working; callers that want the name read file.name.
Describe alternatives you've considered
- A new
FileApiResponse next to BlobApiResponse: forces a template change in apis.mustache and a choice for the user, for no benefit since a File is a Blob.
- Exposing only the helper and leaving
value() unchanged: still leaves every user to call the raw variant.
- Doing it in application code: that is what we do today, duplicated per project.
Additional context
File is a global in browsers and in Node.js 20+ (the CI matrix of the typescript-fetch samples runs Node 20).
PR: #24957
For an operation whose response is a file (
isResponseFile), thetypescript-fetchruntime returns the body throughBlobApiResponse.value():The name the server advertises in
Content-Disposition(attachment; filename="invoice-2026-09.pdf") is dropped. Any client that wants to save the download under its real name has to bypass the generated method, call thexxxRaw()variant, readraw.headersand parse the header itself, and every project ends up with its own copy of that parsing.Every other runtime in this repository already does it for the user:
typescriptgenerator:ResponseContext.getBodyAsFile()returnsnew File([data], fileName, { type })withfileNameread fromcontent-disposition(typescript/http/http.mustache).okhttp-gson,jersey2/3,apache-httpclient,feign,native,vertx):prepareDownloadFilenames the temp file fromContent-Disposition.api_client.mustache,__deserialize_file): idem.ClientUtils,FileParameter): idem.typescript-fetchis the exception.Describe the solution you'd like
Keep
BlobApiResponse, make it return aFilenamed afterContent-Disposition(empty name when the header is absent,typetaken from the blob):plus an exported
parseContentDispositionFilename(headers)handling the RFC 5987filename*=UTF-8''...form first and the plainfilename=form otherwise.Backward compatible:
FileextendsBlob, so the generated methods still satisfy their declaredPromise<Blob>return type and existing callers keep working; callers that want the name readfile.name.Describe alternatives you've considered
FileApiResponsenext toBlobApiResponse: forces a template change inapis.mustacheand a choice for the user, for no benefit since aFileis aBlob.value()unchanged: still leaves every user to call the raw variant.Additional context
Fileis a global in browsers and in Node.js 20+ (the CI matrix of the typescript-fetch samples runs Node 20).PR: #24957