优化TypeScript模板字面量与映射类型的API类型推断实现
优化TypeScript API类的路由类型推断逻辑
我想用TypeScript类型系统构建一个API类,能根据传入的后端路由字符串自动推断返回数据类型。但部分路由包含数字参数,当前实现需要写大量条件类型分支,新增路由时会越来越繁琐。
现有实现代码:
// 假设已定义这些接口 interface IAkey {} interface IUserDevice {} interface IMessageForwarding {} type ResourceType = 'api_keys' | 'message_forwardings' | `devices/${number}/user_devices` type ResourceMap = { api_keys: IAkey[], user_devices: IUserDevice[], message_forwardings: IMessageForwarding[] } type DataType<T extends ResourceType> = T extends keyof ResourceMap ? ResourceMap[T] : T extends `devices/${number}/user_devices` ? ResourceMap['user_devices'] : never class RestApiService<T extends ResourceType> { resource: T data: DataType<T> constructor(resource: T){ this.resource = resource } // 请求方法示例 async get(): Promise<DataType<T>> { // 实际请求逻辑 return {} as DataType<T> } }
当前实现可以正常工作:
const apiKeysData = new RestApiService('api_keys').data // 推断为 IAkey[] new RestApiService('some_route') // 类型错误:'some_route' 不属于 ResourceType const userDeviceData = new RestApiService('devices/1234/user_devices').data // 推断为 IUserDevice[]
但新增更多带模板字面量的路由(比如users/${number}/profile、orders/${number}/items)时,DataType的条件类型分支会越来越多,维护成本很高。有没有办法优化这个泛型类型,不用每次加路由都新增条件分支?
优化方案
可以通过统一的路由模板到资源类型的映射,结合TypeScript的模板字面量类型匹配能力解决这个问题,彻底避免手写大量条件分支。
具体实现步骤
定义路由模板与数据类型的直接映射
创建一个包含所有路由(包括带参数的模板)的映射表,直接关联路由到对应的数据类型:// 假设已定义这些接口 interface IAkey {} interface IUserDevice {} interface IMessageForwarding {} interface IUserProfile {} interface IOrderItem {} // 路由模板 -> 对应数据类型的映射 type RouteTypeMap = { 'api_keys': IAkey[], 'message_forwardings': IMessageForwarding[], `devices/${number}/user_devices`: IUserDevice[], `users/${number}/profile`: IUserProfile[], `orders/${number}/items`: IOrderItem[] }自动推导合法路由类型
无需手动维护ResourceType,直接从映射表的键中提取所有合法路由:type ResourceType = keyof RouteTypeMap简化数据类型推断逻辑
直接通过路由类型从映射表中取值,完全省去条件分支:type DataType<T extends ResourceType> = RouteTypeMap[T]重构API类
现在API类可以直接复用上述类型,逻辑更简洁:class RestApiService<T extends ResourceType> { resource: T data: DataType<T> constructor(resource: T){ this.resource = resource } async get(): Promise<DataType<T>> { return {} as DataType<T> } }
验证效果
// 正确推断数据类型 const apiKeysData = new RestApiService('api_keys').data // IAkey[] const userDeviceData = new RestApiService('devices/1234/user_devices').data // IUserDevice[] const profileData = new RestApiService('users/567/profile').data // IUserProfile[] // 非法路由自动报错 new RestApiService('invalid_route') // 类型错误:'invalid_route' 不能赋值给 ResourceType
扩展优势
- 新增路由时,只需在
RouteTypeMap中添加对应模板和类型,无需修改其他逻辑 - 路由模板的参数类型(如
${number})可直接限制传入路由的格式合法性 - 类型推断完全由TypeScript自动处理,避免手动维护条件分支的遗漏或错误
内容的提问来源于stack exchange,提问作者s.Bergmann
相关产品推荐
相关产品推荐

