如何不使用自定义编译器,用TypeScript从路由字符串自动推导URL参数类型?
可以实现,无需使用ttypescript或自定义编译插件,仅需TypeScript 4.1及以上版本,用其原生支持的模板字面量类型、递归条件类型即可达到预期效果。
实现步骤
1. 定义路径参数提取工具类型
先编写通用工具类型,用于从路由字符串中自动提取所有:开头的路径参数,生成对应的params类型:
// 提取路由中的路径部分(去掉前面的GET/POST等请求方法) type ExtractPath<RouteStr extends string> = RouteStr extends `${string} ${infer Path}` ? Path : RouteStr; // 递归解析路径,提取所有:xxx参数,生成params类型 type PathParams<Path extends string> = // 匹配路径中间的:参数/xxx格式 Path extends `${string}:${infer Param}/${infer Rest}` ? { [K in Param | keyof PathParams<Rest>]: string } // 匹配路径末尾的:参数格式 : Path extends `${string}:${infer Param}` ? { [K in Param]: string } // 没有参数的情况返回空对象 : Record<string, never>;
2. 增强原插件的类型定义
基于typed-fastify原有的Schema和Service类型,封装一层自动注入params的增强类型:
import { Schema, Service as OriginalService } from '@coobaha/typed-fastify'; // 遍历所有路由,自动给每个路由补充params类型 type EnhancedPaths<RawPaths extends Record<string, any>> = { [RouteKey in keyof RawPaths]: Omit<RawPaths[RouteKey], 'request'> & { request?: Omit<RawPaths[RouteKey]['request'], 'params'> & { // 如果用户手动定义了params会优先使用手动定义的,否则用自动生成的 params?: RawPaths[RouteKey]['request']['params'] extends Record<string, any> ? RawPaths[RouteKey]['request']['params'] : PathParams<ExtractPath<RouteKey & string>> } } } // 对外暴露的增强Service类型,直接替换原有Service使用即可 type EnhancedService<RawSchema extends { paths: Record<string, any> }> = OriginalService<{ paths: EnhancedPaths<RawSchema['paths']> } & Omit<Schema, 'paths'>>
使用示例
和预期写法完全一致,无需手动定义params即可获得正确的类型校验:
interface ExampleSchema { paths: { 'GET /test/:testId': { // 仅需定义其他属性,无需显式写params response: { 200: { content: { 'application/json': { id: string } } } } }; 'POST /test/:testId/:userId': {} }; } const exampleService: EnhancedService<ExampleSchema> = { 'GET /test/:testId': (req, reply) => { const testId = req.params.testId // 类型校验通过,类型为string const testName = req.params.testName // 类型校验失败,提示属性不存在 }, 'POST /test/:testId/:userId': (req, reply) => { const { testId, userId } = req.params // 两个参数都能正确推断为string类型 } };
兼容说明
如果部分参数需要自定义类型(比如数字类型参数),直接手动定义params即可,手动定义的优先级高于自动生成的类型,完全兼容原有写法。
内容的提问来源于stack exchange,提问作者Valerio Leo
相关产品推荐
相关产品推荐

