如何实现TypeScript类型安全的多函数调用targetFunction
问题描述
我正在构建一个库,需要实现一个targetFunction函数,使其能与以下接口实现对象交互:
const testInterfaceImpl = { testSN: (a: string, n: number) => { return "" }, testN: (n: number) => { return }, test: () => { return 0 } }
该函数的调用示例如下:
const [stringResult0] = targetFunction([ ["testSN", "", 6] ]) const [stringResult0, voidResult1, stringResult2] = targetFunction([ ["testSN", "john", 5], ["test"], ["testSN", "doe", 7] ]) const [numberResult0, _] = targetFunction([ ["testN", 5], ["testSN", 999999] ]) // TS 应该在这里报错,因为["testSN", 5]参数不符合类型
具体需求:
- 函数需能推断对象的可用函数名、对应参数及返回类型
- 支持传入多个包含函数名和对应参数的可变元组
- 返回与传入参数长度一致的带类型结果数组
- 错误提示或智能感知需显示所选函数的参数类型
我已实现支持单次调用的类型安全版本,但多函数调用版本存在类型校验失效问题,部分错误调用无法被TypeScript检测到,需要解决方案。
解决方案
通过元组映射和条件类型可实现多调用场景的类型安全,核心是对每个调用元组单独做类型校验,并映射出对应返回类型数组:
// 定义合法的调用元组类型:[函数名, ...参数列表] type CallEntry<T extends Record<string, (...args: any[]) => any>> = { [K in keyof T]: [K, ...Parameters<T[K]>] }[keyof T]; // 映射调用元组数组到对应的返回类型数组 type CallResult<T extends Record<string, (...args: any[]) => any>, E extends CallEntry<T>[]> = { [I in keyof E]: E[I] extends [infer K extends keyof T, ...infer Args extends Parameters<T[K]>] ? ReturnType<T[K]> : never }; // 实现targetFunction function targetFunction<T extends Record<string, (...args: any[]) => any>, E extends CallEntry<T>[]>( entries: [...E], impl: T = testInterfaceImpl as T ): CallResult<T, E> { return entries.map(([fnName, ...args]) => { return impl[fnName](...args as any); }) as CallResult<T, E>; } // 测试用例 const testInterfaceImpl = { testSN: (a: string, n: number) => { return "" }, testN: (n: number) => {}, test: () => { return 0 } } // 合法调用 const [stringResult0] = targetFunction([ ["testSN", "", 6] ]) const [stringResult0_2, voidResult1, stringResult2] = targetFunction([ ["testSN", "john", 5], ["test"], ["testSN", "doe", 7] ]) // 非法调用:TS会报错,testSN的第一个参数应为string而非number const [numberResult0, _] = targetFunction([ ["testN", 5], ["testSN", 999999] ])
实现说明
CallEntry<T>:生成所有符合接口定义的调用元组类型,确保函数名和参数的合法性。CallResult<T, E>:对传入的调用元组数组做逐位映射,保证返回数组的类型与调用顺序严格对应。[...E]参数:利用TypeScript可变元组推断能力,让编译器准确识别每个调用元组的具体类型,避免联合类型导致的校验失效。- 类型断言:
args as any是因为TypeScript无法自动推导args与目标函数参数的精确匹配,但外层类型约束已确保传入参数合法,无运行时风险。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

