TypeScript如何正确定义返回路由的函数对象类型
问题复现
在应用中定义如下路由对象与路由请求方法时,get函数内部出现类型报错:
const routes = { home: () => '/', user: ({ userId }: { userId: string }) => `/user/${userId}`, } as const; type Routes = typeof routes; type RoutesKey = keyof Routes; type GetParams<Key extends RoutesKey> = Parameters< Routes[Key] >[0] extends undefined ? [undefined?] : [Parameters<Routes[Key]>[0]]; function get<Path extends RoutesKey>(route: Path, ...params: GetParams<Path>) { // 此处报错:...params must be a tuple or passed as a rest parameter return fetch(routes[route](...params)); } // 函数调用侧的类型提示是正常的 get('home'); get('user', { userId: '1' });
调整类型定义的过程中,要么出现新的类型错误,要么丢失类型提示,不希望使用any类型断言绕过问题,疑惑GetParams辅助类型是否未正确返回元组类型。
报错原因
你定义的GetParams在传入具体的单一路由key(比如'home'、'user')时,确实会返回正确的元组类型,这也是函数调用侧类型提示正常的原因。
但在get函数内部,泛型Path是RoutesKey的子类型,在未被窄化到单个具体字面量类型时,TypeScript 不会对条件类型做分布式求值,此时GetParams<Path>会被推导为联合元组类型[undefined?] | [{ userId: string }],TS 无法建立参数和对应路由函数的关联校验,会认为联合元组不能直接作为剩余参数展开传入函数,因此抛出错误。
解决方案
不需要自定义GetParams类型,直接使用TypeScript内置的Parameters工具类型获取路由函数的原始参数元组即可,函数内部仅需做极小范围的类型断言(完全类型安全,不涉及any):
const routes = { home: () => '/', user: ({ userId }: { userId: string }) => `/user/${userId}`, } as const; type Routes = typeof routes; type RoutesKey = keyof Routes; function get<Path extends RoutesKey>( route: Path, ...params: Parameters<Routes[Path]> ) { // 仅将路由函数断言为接收对应参数的同签名函数,无类型安全问题 const routeHandler = routes[route] as (...args: Parameters<Routes[Path]>) => string; return fetch(routeHandler(...params)); } // 调用侧类型校验完全正常 get('home'); // 正常 get('user', { userId: '1' }); // 正常 // 错误传参会被TS精准拦截 // get('home', {}) // 报错:home路由不需要参数 // get('user') // 报错:缺少userId参数 // get('user', { userId: 123 }) // 报错:userId类型不匹配
这个方案完全保留了调用侧的类型提示,没有使用any绕过校验,内部的断言是安全的:因为函数入口已经通过泛型约束保证了传入参数和路由key的匹配,内部只是帮TS确认了这个对应关系。
内容的提问来源于stack exchange,提问作者magnussampson
相关产品推荐
相关产品推荐

