TypeScript中如何实现操作与对应输入匹配的泛型execute函数?
TypeScript泛型函数实现操作与输入的严格类型约束
首先是你定义的类型:
export interface TypedDocumentNode<R, V> {} type ClaimResult = { id: string; status: string; }; type ClaimInput = { code: string; }; type ResetResult = { id: string; userId: string; }; type ResetInput = { email: string; }; type Operations = { Claim: TypedDocumentNode<ClaimResult, ClaimInput>; Reset: TypedDocumentNode<ResetResult, ResetInput>; };
你需要编写泛型函数execute,要求接收Operations的key作为操作名称,同时传入对应操作的输入变量,返回匹配的结果。合法调用示例:
// 合法调用 execute('Reset', { email: 'test@test.io' }); execute('Claim', { code: 'test-unique-code' });
但你当前的函数定义允许不匹配的输入(比如给Claim传入Reset的参数结构),当前定义如下:
export async function execute< Operation extends keyof typeof Operations, Document extends Documents[Operation], TResult extends Document extends TypedDocumentNode<infer X, any> ? X : never, TVariables extends Document extends TypedDocumentNode<TResult, infer V> ? V : never, > ( operation: Operation, variables?: TVariables ): Promise<TResult> { // 函数实现部分 }
解决方案
完全可以实现严格的类型约束,核心是通过索引访问类型+条件类型提取,直接关联操作名称对应的结果和输入类型,不需要多余的泛型参数。
简化版实现
export async function execute<Op extends keyof Operations>( operation: Op, variables: Operations[Op] extends TypedDocumentNode<infer R, infer V> ? V : never ): Promise<Operations[Op] extends TypedDocumentNode<infer R, infer V> ? R : never> { // 函数实现部分,根据实际逻辑替换返回值 return {} as any; }
可读性优化版(用辅助类型拆分)
// 辅助类型:根据操作名称提取对应结果类型 type OpResult<Op extends keyof Operations> = Operations[Op] extends TypedDocumentNode<infer R, any> ? R : never; // 辅助类型:根据操作名称提取对应输入类型 type OpVariables<Op extends keyof Operations> = Operations[Op] extends TypedDocumentNode<any, infer V> ? V : never; export async function execute<Op extends keyof Operations>( operation: Op, variables: OpVariables<Op> ): Promise<OpResult<Op>> { // 函数实现部分,根据实际逻辑替换返回值 return {} as any; }
为什么之前的定义失效?
你之前的泛型参数TVariables extends ...是类型约束而非精确类型,TypeScript允许子类型赋值,导致不匹配的输入可能绕过检查。直接用条件类型返回精确的输入类型,而非约束,就能确保传入的变量必须完全匹配对应操作的输入结构。
测试非法调用会触发TypeScript错误(符合预期):
// 报错:类型"{ email: string; }"的参数不能赋给类型"ClaimInput"的参数 execute('Claim', { email: 'test@test.io' });
合法调用则完全正常:
// 无错误 execute('Reset', { email: 'test@test.io' }); execute('Claim', { code: 'test-unique-code' });
内容的提问来源于stack exchange,提问作者Harshal Patil
相关产品推荐
相关产品推荐

