Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 9, 2026

Fix ResolvedRequestOptions.headers typing in request interceptors

Problem: Request interceptors receive options.headers that is runtime Headers but typed as union type that doesn't have .set() method. This causes TypeScript compilation errors in strict mode.

Solution implemented:

  • Add ResolvedRequestOptions interface with headers: Headers to all client bundles (custom-client, client-angular, client-fetch, client-ky, client-next, client-ofetch)
  • Update client-next SSE implementation to convert headers to Headers before calling interceptor
  • Remove unused @ts-expect-error directive that was suppressing the type error
  • Remove unused RequestOptions import from custom-client
  • Simplify type annotations per code review feedback
  • Regenerate examples to sync with type changes
  • Run type checking to validate changes - ✅ PASSED
  • Run tests to ensure nothing breaks - ✅ ALL 234 TESTS PASSED
  • Address all code review feedback
  • Fix CI failure by regenerating examples

Result: Request interceptors can now safely use all Headers methods like .set(), .append(), .delete(), .get(), and .has() without type errors.

CI Status: Examples regenerated and synced. Ready for CI validation.

Original prompt

This section details on the original issue you should resolve

<issue_title>ResolvedRequestOptions.headers should be typed as Headers in request interceptors</issue_title>
<issue_description>### 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</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@changeset-bot
Copy link

changeset-bot bot commented Feb 9, 2026

⚠️ No Changeset found

Latest commit: f292aa7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Feb 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hey-api-docs Ready Ready Preview, Comment Feb 10, 2026 6:52am

Request Review

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
…nused imports

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
…properties

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix type of ResolvedRequestOptions.headers in request interceptors Narrow ResolvedRequestOptions.headers type to Headers in request interceptors Feb 9, 2026
Copilot AI requested a review from mrlubos February 9, 2026 14:42
@mrlubos
Copy link
Member

mrlubos commented Feb 10, 2026

@copilot make sure CI checks pass

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot make sure CI checks pass

CI checks fixed in commit f292aa7. The generated examples were out of sync with the type definition changes. All examples have been regenerated and the pnpm examples:check command now passes.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ResolvedRequestOptions.headers should be typed as Headers in request interceptors

2 participants