TypeScript路由参数类型校验问题:禁止不符参数传入函数
问题:TypeScript路由参数类型校验失效
我把所有API路由归到一个enum里,用来根据URL参数生成对应对象。但在将对象传入接受不同参数结构的函数时,TypeScript错误地允许了本不应被接受的对象——比如传入缺少目标路由必填属性的params对象,编译器却没报错。
enum Routes { WorkflowRun = "/workflows/:workflowId/runs/:runId", WorkflowRunTest = "/workflows/:foo/runs/:bar", } type Split<S extends string, SEP extends string, Result extends string[] = []> = S extends `${infer _}` ? S extends `${infer L}${SEP}${infer R}` ? Split<R, SEP, [...Result, L]> : S extends '' ? SEP extends '' ? [...Result] : [S] : [...Result, S] : string[] type Parts = Split<Routes.WorkflowRun, '/'> // ^? ["", "workflow", ":workflowId", "runs", ":runId"] type ParamValue = number | string type GetRouteParams<Route extends string> = { [K in Split<Route, '/'>[number] as K extends `:${infer Param}` ? Param : never]: ParamValue } type Test = GetRouteParams<Routes.WorkflowRun> // { workflowId: string | number; runId: string | number } 👍 function replaceParams<R extends Routes, P extends GetRouteParams<R>>(route: R, params: P): string { let finalRoute = route for (const key in params) { const value = params[key] as ParamValue finalRoute.replace(key, value.toString()) } return finalRoute } function getWorkflowRun(params: GetRouteParams<Routes.WorkflowRun>) { const route = replaceParams(Routes.WorkflowRunTest, params) // 为何缺少foo和bar属性的params仍被允许? }
修复方案
问题根源在于replaceParams的泛型约束过于宽松:P extends GetRouteParams<R>允许传入目标参数类型的子类型,而TypeScript的结构类型系统会误判无重叠属性的对象为符合约束。
步骤1:收紧泛型约束
直接要求params严格匹配目标路由对应的参数类型,去掉多余的泛型参数P:
function replaceParams<R extends Routes>(route: R, params: GetRouteParams<R>): string { let finalRoute = route for (const key in params) { const paramKey = `:${key}`; // 路由参数带冒号,需匹配完整格式 finalRoute = finalRoute.replace(paramKey, params[key].toString()) } return finalRoute }
步骤2:修正字符串替换逻辑
原代码中finalRoute.replace(...)不会修改原字符串(replace返回新字符串),且未匹配路由参数的冒号前缀,修正后才能正确替换参数。
效果验证
修改后,传入不符合目标路由参数结构的对象会直接触发TypeScript错误:
function getWorkflowRun(params: GetRouteParams<Routes.WorkflowRun>) { const route = replaceParams(Routes.WorkflowRunTest, params) // 报错:类型“GetRouteParams<Routes.WorkflowRun>”的参数不能赋给类型“GetRouteParams<Routes.WorkflowRunTest>”的参数。 // 属性“foo”在类型“GetRouteParams<Routes.WorkflowRun>”中缺失,但在类型“GetRouteParams<Routes.WorkflowRunTest>”中是必需的。 }
内容的提问来源于stack exchange,提问作者nook
相关产品推荐
相关产品推荐

