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
Problem
When using
pathcatwith a path that has no route params (e.g./organizations/events/views/stream),Query<string>does not accept array values despiteQueryValuesupporting them.Cause
Query<Template>is defined as:When
Template = string,ExtractRouteParams<string>resolves tostring(the catch-all branch). This makes the type:The intersection of
Record<string, ParamValue>andRecord<string, QueryValue>collapses toRecord<string, ParamValue>becauseParamValue & QueryValue=ParamValue & (ParamValue | ParamValue[])=ParamValue. Arrays are eliminated.For paths with route params like
/:id, only the:idkey requiresParamValueand other keys acceptQueryValue— so arrays work fine there.Expected behavior
Query<string>should acceptQueryValue(including arrays) for all keys, sincepathcathandles arrays correctly at runtime.Suggested fix
The
ExtractRouteParams<string>catch-all should resolve toneverinstead ofstring:This way
Query<string>becomesRecord<never, ParamValue> & Record<string, QueryValue>=Record<string, QueryValue>, which correctly accepts arrays.Environment