TypeScript中如何正确定义多层泛型类型的请求数组参数?
解决方案:用联合类型实现多类型请求数组的类型约束
要让send函数接收不同服务、不同方法的Request实例数组,同时保留类型检查,核心是生成所有合法Request类型的联合类型,而非用单一泛型或错误的交叉类型。
步骤1:定义辅助联合类型
先创建一个辅助类型,自动生成TServiceMap下所有合法的Request组合:
export type ServiceMap<T> = { [K in keyof T]: MethodMap<T[K]> } export type MethodMap<T> = { [K in keyof T]: (...args: any) => Promise<any> } interface Request<TServiceMap extends ServiceMap<TServiceMap>, TService extends keyof TServiceMap, TMethod extends keyof TServiceMap[TService]> { service: TService, method: TMethod, // 其他字段 } // 辅助类型:生成所有合法Request的联合类型 type AnyRequest<TServiceMap extends ServiceMap<TServiceMap>> = { [ServiceKey in keyof TServiceMap]: Request<TServiceMap, ServiceKey, keyof TServiceMap[ServiceKey]> }[keyof TServiceMap];
这个类型的逻辑是:遍历TServiceMap的每个服务键,生成该服务对应的所有Request类型,最后通过[keyof TServiceMap]提取所有类型的联合。
步骤2:重构send函数
用辅助联合类型约束请求数组:
function createRequest<TServiceMap extends ServiceMap<TServiceMap>, TService extends keyof TServiceMap, TMethod extends keyof TServiceMap[TService]>(service: TService, method: TMethod): Request<TServiceMap, TService, TMethod> { return {} as Request<TServiceMap, TService, TMethod>; } function send<TServiceMap extends ServiceMap<TServiceMap>>(requests: AnyRequest<TServiceMap>[]): void { // 实现发送逻辑 }
验证示例
用你的测试代码验证,类型检查会正常工作:
interface UserService { login: (userName: string) => Promise<string>, logout: () => Promise<void> } interface TicketService { create: (ticketNumber: number) => Promise<void> } interface MyServices { user: UserService, ticket: TicketService, } const requests = [ createRequest<MyServices, 'user', 'login'>('user', 'login'), createRequest<MyServices, 'ticket', 'create'>('ticket', 'create') ] send<MyServices>(requests); // 类型检查通过 // 错误示例:传入不存在的方法会触发类型报错 const invalidRequests = [ createRequest<MyServices, 'user', 'create'>('user', 'create') // 报错:'create'不是UserService的方法 ]
为什么之前的写法失败?
- 第一种泛型写法:
TService和TMethod是全局泛型,会强制数组中所有元素的服务和方法类型完全一致,不符合多请求场景。 - 第二种写法:
keyof TServiceMap[keyof TServiceMap]是所有服务方法名的交叉联合,没有绑定到具体服务,TypeScript无法找到同时属于所有服务的方法,因此推断为never。
内容的提问来源于stack exchange,提问作者CodeMonkey
相关产品推荐
相关产品推荐

