Skip to content

ResolvedRequestOptions.headers should be typed as Headers in request interceptors #3372

@martinjuhasz

Description

@martinjuhasz

Description

The Next.js client documentation shows request interceptors using options.headers.set() directly:

client.interceptors.request.use((options) => {
  options.headers.set('Authorization', 'Bearer <my_token>')
})

This does not compile with strict: true because options.headers is typed as RequestInit['headers'] | Record<string, ...> (inherited from CoreConfig), which is a union that doesn't expose .set().

At runtime, headers is always a Headers instance — mergeHeaders() in utils.ts always returns new Headers(). But the type doesn't reflect this.

Workaround:

const headers = options.headers as unknown as Headers
headers.set('Authorization', 'Bearer token')

Suggested fix

ResolvedRequestOptions (the type passed to request interceptors) should narrow headers to Headers:

export interface ResolvedRequestOptions<
  ThrowOnError extends boolean = boolean,
  Url extends string = string,
> extends RequestOptions<unknown, ThrowOnError, Url> {
  headers: Headers // ← narrowed, since mergeHeaders() always produces Headers
  serializedBody?: string
}

The broad union on Config.headers is correct (users should be able to pass various formats), but after resolving (i.e., in interceptors), it's always Headers.

Type chain for reference

Client.interceptors = Middleware<Response, unknown, ResolvedRequestOptions>
  → ReqInterceptor<ResolvedRequestOptions> = (options: ResolvedRequestOptions) => void
    → ResolvedRequestOptions extends RequestOptions
      → RequestOptions extends Config (next/types.ts)
        → Config extends CoreConfig
          → CoreConfig.headers?: RequestInit['headers'] | Record<string, ...>

Versions

  • @hey-api/openapi-ts: 0.92.3
  • TypeScript: 5.9.3
  • tsconfig.json: strict: true

Reproducible example or configuration

with a next client

import { createClient } from './generated/client/client.gen'

const client = createClient({ baseUrl: 'https://example.com' })

client.interceptors.request.use((options) => {
  // TS Error: Property 'set' does not exist on type '...'
  options.headers.set('Authorization', 'Bearer token')
})

OpenAPI specification (optional)

No response

System information (optional)

No response

Metadata

Metadata

Labels

bug 🔥Broken or incorrect behavior.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions