Extracting Route Params

Parsing a String Type

With infer inside a template literal type you can parse string types. Express, Hono and TanStack Router use this to type route parameters from the path string itself.

Example

type ParamNames<P extends string> =
  P extends `${string}:${infer Name}/${infer Rest}` ? Name | ParamNames<Rest>
  : P extends `${string}:${infer Name}` ? Name
  : never;

type Params<P extends string> = { [K in ParamNames<P>]: string };

function buildPath<P extends string>(pattern: P, params: Params<P>): string {
  return pattern.replace(/:(\w+)/g, (_, key: string) => encodeURIComponent((params as Record<string, string>)[key]));
}

const productUrl = buildPath("/products/:id", { id: "42" });
const reviewUrl = buildPath("/shops/:shopId/products/:productId/reviews", { shopId: "s9", productId: "p 1" });
console.log(productUrl);
console.log(reviewUrl);

Output

/products/42
/shops/s9/products/p%201/reviews

Missing Param

Example

Forgetting productId is a compile error (TS2345); the parameter list was read from the string.

type ParamNames<P extends string> =
  P extends `${string}:${infer Name}/${infer Rest}` ? Name | ParamNames<Rest>
  : P extends `${string}:${infer Name}` ? Name
  : never;
type Params<P extends string> = { [K in ParamNames<P>]: string };
declare function buildPath<P extends string>(pattern: P, params: Params<P>): string;

buildPath("/shops/:shopId/products/:productId", { shopId: "s9" });

Compiler output (tsc)

Line 8: error TS2345: Argument of type '{ shopId: string; }' is not assignable to parameter of type 'Params<"/shops/:shopId/products/:productId">'.
  Property 'productId' is missing in type '{ shopId: string; }' but required in type 'Params<"/shops/:shopId/products/:productId">'.