You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript路由函数参数推断与Plugin类型校验报错问题

路由参数推断与Plugin类型校验问题解决

问题

需要实现一个router函数,能够从传入的路径中推断路由参数,并校验plugin的函数结构,但当前代码运行时出现类型错误。

现有代码

interface RouteConfig<Path extends string = string> {
  hash?: string;
  name: string;
  path: Path;
  plugins?: Array<RouterPlugin<Path>>;
  search?: string;
}

type Route<Path extends string> = RouteConfig<Path> & {
  plugins: Array<RouterPlugin<Path>>;
};

interface RouteContext<Params = { [key: string]: string | undefined }, Query = Record<string, string>> {
  name: string;
  params: Params;
  query: Query;
  url: URL;
}

type RouterPlugin<Path extends string> = (context: RouteContext<PathParams<Path>>) => void | Promise<void>;

type PathParams<Path extends string> = string extends Path
  ? Record<string, string | undefined>
  : // eslint-disable-next-line @typescript-eslint/no-unused-vars
    Path extends `${infer _Start}:${infer Param}/${infer Rest}`
    ? { [K in Param | keyof PathParams<Rest>]: string }
    : // eslint-disable-next-line @typescript-eslint/no-unused-vars
      Path extends `${infer _Start}:${infer Param}`
      ? { [K in Param]: string }
      : Record<string, unknown>;


function createRouter<T extends RouteConfig>(config: T[]) {
  //
}

const router = createRouter([
  {
    name: 'task',
    path: '/tasks/:id',
    plugins: [(ctx: { params: { id: string }}) => {}], // 此处报错
  },
]);

错误信息

Property 'id' is missing in type 'Record<string, string | undefined>' but required in type '{ id: string; }'

解决方案

核心问题

createRouter的泛型约束T extends RouteConfig会将所有路由项的Path统一解析为string类型(因RouteConfig默认泛型为string),导致PathParams<Path>触发兜底分支,返回Record<string, string | undefined>,与手动指定的{ params: { id: string } }类型不匹配。

修改方案

调整createRouter的泛型定义,让TypeScript保留每个路由项的具体Path字面量类型:

function createRouter<T extends readonly RouteConfig[]>(config: T) {
  return config as {
    [K in keyof T]: T[K] extends RouteConfig<infer P> ? Route<P> : never;
  };
}

同时,删除plugins中手动指定的ctx类型,TypeScript会自动根据path推断正确的RouteContext结构:

const router = createRouter([
  {
    name: 'task',
    path: '/tasks/:id',
    plugins: [(ctx) => {
      // ctx.params.id 会被正确推断为 string 类型
      console.log(ctx.params.id);
    }],
  },
]);

原理说明

修改后的泛型会逐个解析数组中每个路由项的Path类型,触发PathParams的条件类型分支,精准推断出路径中的参数结构,从而让RouterPlugin的上下文类型与路径参数完全匹配。


内容的提问来源于stack exchange,提问作者Andy Jessop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:36:07