如何让TypeScript动态推断动态生成路由函数的参数类型?
如何让TypeScript动态推断路由函数的参数类型?
我需要将路由对象动态转换为同时支持静态和动态调用的函数,例如针对路由/posts/:id,可以这样使用:
// 静态声明,输出为 /posts/:id <Route path={ROUTES.singlePost()} /> // 动态调用,id=1时输出为 /posts/1 navigate(ROUTES.singlePost(someId))
当前的实现代码如下:
const rawRoutes = [{ key: "singlePost", path: "/posts/:id" }] export const ROUTES = rawRoutes.reduce( (acc, item) => ({ ...acc, [item.key]: buildRouteFn(item.path), }), {} as Record<IRouteKey, (...args: string[]) => string>, ) function buildRouteFn(path: string) { return (...args: string[]) => args.reduce((_path, arg) => _path.replace(/:\w+/, arg), path) } type IRouteKey = typeof rawRoutes[number]["key"]
目前的问题是,TypeScript的自动补全无法显示实际的参数信息(比如id?: string)。请问有没有办法让TypeScript动态推断这些参数类型?
我可以退而求其次使用如下方案,但这种拆分静态和动态函数的方式可读性和维护性较差:
export const ROUTES = { singlePostStatic: () => "/posts/:id", singlePostDynamic: (id: string) => `/posts/${id}`, }
解决方案:利用TypeScript模板字符串类型实现动态参数推断
通过自定义类型解析路径中的参数,并结合映射类型为每个路由生成精确的函数类型,可以实现自动补全和类型检查。
1. 定义参数提取与函数类型
// 提取路径中的参数名,返回参数名组成的元组类型 type ExtractRouteParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}` ? [Param, ...ExtractRouteParams<Rest>] : T extends `${string}:${infer Param}` ? [Param] : []; // 根据参数元组生成对应的函数类型 type RouteFunction<T extends string> = ExtractRouteParams<T> extends [] ? () => T : (...args: { [K in keyof ExtractRouteParams<T>]: string }) => string;
2. 重构路由定义与生成逻辑
// 使用const断言让TypeScript保留路径的字面量类型 const rawRoutes = [{ key: "singlePost", path: "/posts/:id" }] as const; // 定义ROUTES的类型:每个key对应其路径的RouteFunction type Routes = { [K in typeof rawRoutes[number]["key"]]: RouteFunction<Extract<typeof rawRoutes[number], { key: K }>["path"]> }; function buildRouteFn<T extends string>(path: T): RouteFunction<T> { return (...args: string[]) => args.reduce((_path, arg) => _path.replace(/:\w+/, arg), path) as any; } // 生成ROUTES并断言为正确的类型 export const ROUTES = rawRoutes.reduce( (acc, item) => ({ ...acc, [item.key]: buildRouteFn(item.path), }), {} as Routes );
效果说明
- 调用
ROUTES.singlePost()时,TypeScript会提示无参数,返回/posts/:id - 调用
ROUTES.singlePost("123")时,会提示需要传入id: string参数,返回/posts/123 - 多参数路由(比如
/posts/:id/comments/:commentId)也能自动推断出(id: string, commentId: string) => string的函数类型
内容的提问来源于stack exchange,提问作者goldylucks
相关产品推荐
相关产品推荐

