TypeScript中带函数提示的泛型类型与属性接口实现
TypeScript Handler类类型约束与自动补全实现
需求概述
需要实现一个Handler类,满足:
- 仅接受包含至少一个带返回值函数属性的接口作为泛型参数,无合法属性的接口会触发报错
send方法参数需严格校验并自动补全:- 第一个参数仅能选择合法的解析器属性(包含带返回值函数的属性)
- 第二个参数仅能选择对应解析器下的带返回值函数
- 第三个参数
payload类型必须匹配所选函数的返回值类型
解决方案代码
// 返回类型定义 interface DataA { value: number; } interface DataB { text: string; } interface DataC { has: boolean; } // 合法解析器:包含至少一个带返回值的函数 interface ResolverA { callA(...args: any): DataA; callB(): DataB; } interface ResolverB { value: number; // 非函数,不会被send方法选中 resolve(): DataC; } // 非法解析器:无带返回值的函数 interface ResolverC { text: string; } // 合法Resolvers接口:包含至少一个合法解析器 interface ResolversShouldWork { has: boolean; // 非解析器属性,不会被send方法选中 resolverA: ResolverA; resolverB: ResolverB; resolverC: ResolverC; // 非法解析器,不会被send方法选中 } // 非法Resolvers接口:无合法解析器 interface ResolversShouldntWorkA { item: object; } // 非法Resolvers接口:仅含非法解析器 interface ResolversShouldntWorkB { resolverC: ResolverC; } // 工具类型:提取类型T中所有值为带返回值函数的键(排除返回void的函数) type PickMethodKeys<T> = { [K in keyof T]: T[K] extends (...args: any) => infer R ? (R extends void ? never : K) : never; }[keyof T]; // 工具类型:判断T是否包含至少一个带返回值的函数,无则返回never type HasAtLeastOneMethod<T> = PickMethodKeys<T> extends never ? never : T; // 工具类型:从Resolvers中提取所有合法解析器的键 type ValidResolverKeys<TResolvers> = { [K in keyof TResolvers]: TResolvers[K] extends HasAtLeastOneMethod<TResolvers[K]> ? K : never; }[keyof TResolvers]; class Handler<TResolvers extends Record<string, unknown>> { // 强制校验泛型参数必须包含至少一个合法解析器 constructor() { type Check = ValidResolverKeys<TResolvers> extends never ? never : unknown; const _check: Check = true; } public send< TResolver extends ValidResolverKeys<TResolvers>, TMethod extends PickMethodKeys<TResolvers[TResolver]>, TPayload extends ReturnType<TResolvers[TResolver][TMethod]> >(resolver: TResolver, method: TMethod, payload: TPayload) { // 业务逻辑实现 } } // 测试用例验证 const handlerA = new Handler<ResolversShouldWork>(); // ✅ 正常通过 const handlerB = new Handler<ResolversShouldntWorkA>(); // ❌ 报错:无合法解析器 const handlerC = new Handler<ResolversShouldntWorkB>(); // ❌ 报错:无合法解析器 handlerA.send('has', null, null); // ❌ 报错:'has'不是合法解析器 handlerA.send('resolverA', 'callA', { value: 5 }); // ✅ 正常,payload匹配DataA handlerA.send('resolverA', 'callB', { text: 'hi' }); // ✅ 正常,payload匹配DataB handlerA.send('resolverB', 'resolve', { has: true }); // ✅ 正常,payload匹配DataC handlerA.send('resolverB', 'value', null); // ❌ 报错:'value'不是带返回值的函数 handlerA.send('resolverC', null, null); // ❌ 报错:'resolverC'不是合法解析器
关键类型解释
PickMethodKeys<T>:遍历类型T的所有键,筛选出值为带返回值函数的键(排除返回void的函数),无符合条件的键时返回never。HasAtLeastOneMethod<T>:判断类型T是否包含至少一个带返回值的函数,若没有则返回never,用于约束解析器的合法性。ValidResolverKeys<TResolvers>:从泛型参数TResolvers中筛选出值为合法解析器的键,无合法解析器时返回never。- 构造函数类型校验:通过
Check类型强制要求TResolvers必须包含至少一个合法解析器,否则创建实例时会触发类型错误。 - send方法泛型约束:
TResolver限制第一个参数只能选合法解析器的键TMethod限制第二个参数只能选对应解析器下的带返回值函数TPayload直接绑定所选函数的返回值类型,确保参数类型匹配
内容的提问来源于stack exchange,提问作者Kleywalker
相关产品推荐
相关产品推荐

