如何优化TypeScript模板字面量类型ExtractParams以避免重复?
优化TypeScript路径参数提取类型:消除代码重复
我实现了一个用于从URL路径中提取参数的TypeScript类型ExtractParams,代码如下:
type ExtractParams<Path extends string> = Path extends `${infer Start}/:${infer Param}/${infer Rest}` ? ExtractParams<Start> & ExtractParams<Rest> & { [Key in Param]: string; } : Path extends `${infer Start}/:${infer Param}` ? ExtractParams<Start> & { [Key in Param]: string } : {}
这个类型可以正常工作,比如:
type X1 = ExtractParams<'/courses/:courseId/classes/:classId'> // ^? { classId: string; } & { courseId: string; }
不过当前实现存在代码重复问题:两个条件分支逻辑高度相似,只是一个匹配路径中间的参数(后面跟/${infer Rest}),另一个匹配路径末尾的参数(后面无内容)。我希望将/${infer Rest}部分设为可选,以此消除重复代码。
优化方案
可以通过模板字面量的灵活匹配特性,将两个分支合并为一个,核心思路是让参数后面的内容既可以是空字符串(对应末尾参数),也可以是带/的剩余路径(对应中间参数)。优化后的ExtractParams类型如下:
type ExtractParams<Path extends string> = Path extends `${infer Start}/:${infer Param}${infer Rest}` ? ExtractParams<Start> & (Rest extends `/${infer R}` ? ExtractParams<R> : {}) & { [Key in Param]: string } : {}
原理解释
- 模板字面量
${infer Start}/:${infer Param}${infer Rest}会匹配所有包含:Param的路径片段:- 当
Param在路径末尾时,Rest会被推断为空字符串,此时Rest extends/${infer R}不成立,直接返回ExtractParams& { [Key in Param]: string }`,和原末尾分支逻辑一致。 - 当
Param在路径中间时,Rest会被推断为/${infer R}(比如路径/courses/:courseId/classes/:classId中,匹配:courseId时Rest为/classes/:classId),此时会递归处理R(即classes/:classId),同时合并参数类型,和原中间分支逻辑一致。
- 当
结合Expand类型的最终代码
之前已经用Expand类型解决了交叉类型的展开问题,最终完整代码如下:
type Expand<T> = T extends infer U ? { [K in keyof U]: U[K] } : never; type ExtractParams<Path extends string> = Path extends `${infer Start}/:${infer Param}${infer Rest}` ? ExtractParams<Start> & (Rest extends `/${infer R}` ? ExtractParams<R> : {}) & { [Key in Param]: string } : {} type Params<Path extends string> = Expand<ExtractParams<Path>>; // 示例 type X1 = Params<'/courses/:courseId/classes/:classId'>; // ^? { courseId: string; classId: string }
内容的提问来源于stack exchange,提问作者Vishal
相关产品推荐
相关产品推荐

