Skip to content

Query<string> type rejects arrays for paths without route params #7

@antoinekm

Description

@antoinekm

Problem

When using pathcat with a path that has no route params (e.g. /organizations/events/views/stream), Query<string> does not accept array values despite QueryValue supporting them.

import { pathcat } from 'pathcat';
import type { Query } from 'pathcat';

// Works at runtime — produces ?events=a&events=b
pathcat('https://api.example.com', '/stream', { events: ['a', 'b'] });

// But TypeScript rejects it:
const params: Query<string> = { events: ['a', 'b'] };
// Error: Type 'string[]' is not assignable to type 'ParamValue'

Cause

Query<Template> is defined as:

type Query<Template extends string> = Record<ExtractRouteParams<Template>, ParamValue> & Record<string, QueryValue>;

When Template = string, ExtractRouteParams<string> resolves to string (the catch-all branch). This makes the type:

Record<string, ParamValue> & Record<string, QueryValue>

The intersection of Record<string, ParamValue> and Record<string, QueryValue> collapses to Record<string, ParamValue> because ParamValue & QueryValue = ParamValue & (ParamValue | ParamValue[]) = ParamValue. Arrays are eliminated.

For paths with route params like /:id, only the :id key requires ParamValue and other keys accept QueryValue — so arrays work fine there.

Expected behavior

Query<string> should accept QueryValue (including arrays) for all keys, since pathcat handles arrays correctly at runtime.

Suggested fix

The ExtractRouteParams<string> catch-all should resolve to never instead of string:

export type ExtractRouteParams<T extends string> = string extends T
  ? never  // was: string
  : T extends `${string}:${infer Param}/${infer Rest}`
  ? Param | ExtractRouteParams<Rest>
  : T extends `${string}:${infer Param}`
  ? Param
  : never;

This way Query<string> becomes Record<never, ParamValue> & Record<string, QueryValue> = Record<string, QueryValue>, which correctly accepts arrays.

Environment

  • pathcat v1.5.1
  • TypeScript 5.9.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions