模板字面量类型+条件类型可在编译期捕获路径参数类型错误,如将/user/123/post/abc中abc应为number的错误提前暴露;需用infer提取参数名并递归处理多级参数,配合类型映射实现id: number等约束。

能直接在编译期捕获 /user/123/post/abc 这类路径中 abc 应为 number 的错误,而不是等运行时 404 或后端报 500 —— 这就是模板字面量类型 + 条件类型组合能干的实事。
用 `/users/:id` 这种写法定义路由类型,TS 默认不认
TypeScript 不会把字符串字面量 "/users/:id" 自动识别为“带参数的路径”,它只当普通字符串。必须显式告诉 TS:冒号后是参数占位符,且要提取它的名字和约束类型。
- 不能直接写
type UserRoute = "/users/:id"—— 这只是个固定字符串,id没有类型意义 - 正确做法是用模板字面量配合
infer,例如:type RouteParams= T extends `${string}/:${infer K}` ? { [k in K]: string } : {} - 关键点:
${infer K}是唯一能从字符串中“抓出”参数名的方式;而K后续可被映射为对象键 - 如果路径含多级参数(如
/posts/:postId/comments/:commentId),需递归处理,否则只会提取第一个:postId
ExtractParams 怎么自动推导成 { id: string; postId: string }
靠条件类型 + 递归展开。核心是让 TS 把路径按 /: 切开,逐段 infer 参数名,并累积到一个对象类型里。
- 基础递归结构:
T extends `${string}/:${infer K}/${infer Rest}` ? { [k in K]: string } & ExtractParams: ... - 注意
Rest必须再进一次ExtractParams,否则第二层参数不会出现 - 终止条件要覆盖单参数(
/${string}/:${infer K})和无参数(string)两种情况,漏掉会导致类型变any - 实际使用时,
ExtractParams推导出{ id: string },但如果你希望id是number,就得额外加约束逻辑(见下一条)
让 :id 对应 number 而非默认 string,需要手动标注
模板字面量本身不解析语义,:id 和 :idNum 在类型层面没区别。要实现数字校验,得引入额外标记机制。
完整的Galileo AI平台 TypeScript/JS SDK 参考,用于评估、观察和保护 GenAI 应用。适用于构建 Node.js 或 TypeScript 项目。
- 常见方案:改用大括号语法,如
/users/{id:number}/posts/{postId:number},再用正则式匹配{(\w+):(\w+)}提取名与类型名 - 然后通过映射类型查表:
type TypeMap = { number: number; string: string; boolean: boolean },拼出{ [k in K]: TypeMap[T] } - 更轻量的做法是约定后缀,比如
:idNum表示 number,:idStr表示 string,再用条件类型做后缀剥离 - ⚠️ 注意:TypeScript 无法在类型层面执行 parseInt,所以所有“数字参数”最终仍需运行时转换,类型层只负责拒绝非数字字符串字面量(如
"abc")
生成完整路由联合类型时,keyof 和 as const 容易混淆
你可能想把所有合法路径收集成一个联合类型,比如 "/users/:id" | "/posts/:id",但若来源是对象 key,容易掉坑。
- 错误写法:
const routes = { user: "/users/:id", post: "/posts/:id" }; type AllRoutes = keyof typeof routes→ 得到"user" | "post",不是路径字符串 - 正确写法:先用
as const固定值,再取值类型:const routes = { user: "/users/:id", post: "/posts/:id" } as const; type AllRoutes = typeof routes[keyof typeof routes] - 更推荐直接定义联合类型:
type KnownRoutes = "/users/:id" | "/posts/:id",避免运行时对象干扰类型推导 - 如果路由来自配置文件(JSON),需用
declare module "*.json"+as const导入,否则 JSON 默认是any
真正难的不是写出 ExtractParams,而是当路由嵌套变深、参数类型混合(string/number/id[])、还要兼容 SSR 数据预取时,类型递归深度和错误提示可读性会迅速下滑——这时候宁愿拆成两个工具类型,也不硬塞进一个泛型里。










