Repository: honojs/hono
File: src/client/types.ts (TypeScript)

Code before:
   * Custom function to serialize query parameters into URLSearchParams.
   * By default, arrays are serialized as multiple parameters with the same key (e.g., `key=a&key=b`).
   * You can provide a custom function to change this behavior, for example to use bracket notation (e.g., `key[]=a&key[]=b`).
   *
   * @example
   * ```ts
   * const client = hc('http://localhost', {
   *   buildSearchParams: (query) => {
   *     return new URLSearchParams(qs.stringify(query))
   *   }
   * })
   * ```
   */
  buildSearchParams?: BuildSearchParamsFn
} & (keyof T extends never
  ? {
      headers?:
        | Record<string, string>
        | (() => Record<string, string> | Promise<Record<string, string>>)
    }
  : {
      headers: T | (() => T | Promise<T>)
    })

export type ClientRequest<Prefix extends string, Path extends string, S extends Schema> = {
  [M in keyof ExpandAllMethod<S>]: ExpandAllMethod<S>[M] extends Endpoint & { input: infer R }
    ? R extends object
      ? HasRequiredKeys<R> extends true
        ? (args: R, options?: ClientRequestOptions) => Promise<ClientResponseOfEndpoint<ExpandAllMethod<S>[M]>>
        : (args?: R, options?: ClientRequestOptions) => Promise<ClientResponseOfEndpoint<ExpandAllMethod<S>[M]>>
      : never
    : never
} & {
  $url: <
    const Arg extends
      | (S[keyof S] extends { input: infer R }
          ? R extends { param: infer P }
            ? R extends { query: infer Q }
              ? { param: P; query: Q }
              : { param: P }
            : R extends { query: infer Q }
              ? { query: Q }
              : {}
          : {})
      | undefined = undefined,
  >(
    arg?: Arg
  ) => HonoURL<Prefix, Path, Arg>
} & (S['$get'] extends { outputFormat: 'ws' }
    ? S['$get'] extends { input: infer I }
      ? {

Review comment (performance):
## Performance Measurement

Measured TypeScript type-checking performance before and after this change, just to make sure.

### Environment

- TypeScript: `tsc --noEmit --extendedDiagnostics`
- Machine: macOS (Darwin 25.2.0)

### Results

| Metric | Before | After | Diff |
|--------|--------|-------|------|
| Memory used | 704,748K | 713,348K | +8,600K (+1.2%) |
| Check time | 2.76s | 2.74s | -0.02s |
| Total time | 3.28s | 3.38s | +0.10s |

### Analysis

The `ExpandAllMethod<S>` type adds minimal overhead:

- Single conditional type check (`MethodNameAll extends keyof S`)
- Mapped type over 6 keys (`StandardMethods`)
- Built-in `Omit` utility type

No recursive type computation is introduced. The slight memory increase (~1.2%) is negligible and within acceptable bounds.

Code after:
   * Custom function to serialize query parameters into URLSearchParams.
   * By default, arrays are serialized as multiple parameters with the same key (e.g., `key=a&key=b`).
   * You can provide a custom function to change this behavior, for example to use bracket notation (e.g., `key[]=a&key[]=b`).
   *
   * @example
   * ```ts
   * const client = hc('http://localhost', {
   *   buildSearchParams: (query) => {
   *     return new URLSearchParams(qs.stringify(query))
   *   }
   * })
   * ```
   */
  buildSearchParams?: BuildSearchParamsFn
} & (keyof T extends never
  ? {
      headers?:
        | Record<string, string>
        | (() => Record<string, string> | Promise<Record<string, string>>)
    }
  : {
      headers: T | (() => T | Promise<T>)
    })

export type ClientRequest<Prefix extends string, Path extends string, S extends Schema> = {
  [M in keyof ExpandAllMethod<S>]: ExpandAllMethod<S>[M] extends Endpoint & { input: infer R }
    ? R extends object
      ? HasRequiredKeys<R> extends true
        ? (
            args: R,
            options?: ClientRequestOptions
          ) => Promise<ClientResponseOfEndpoint<ExpandAllMethod<S>[M]>>
        : (
            args?: R,
            options?: ClientRequestOptions
          ) => Promise<ClientResponseOfEndpoint<ExpandAllMethod<S>[M]>>
      : never
    : never
} & {
  $url: <
    const Arg extends
      | (S[keyof S] extends { input: infer R }
          ? R extends { param: infer P }
            ? R extends { query: infer Q }
              ? { param: P; query: Q }
              : { param: P }
            : R extends { query: infer Q }
              ? { query: Q }
              : {}
          : {})
      | undefined = undefined,