如何创建支持两种参数形式的基于对象键的递归函数类型?
解决方案:递归函数类型适配键与对象参数
要实现符合需求的递归函数类型,核心是解决非/${string}键对应的对象参数与键名的关联推断问题。以下是完整的实现方案:
类型定义
// 保留你定义的参数类型约束 type Argument<K> = K extends `/${string}` ? K : { [key in K]: string | number }; // 从参数中提取对应的键(区分键字符串和对象参数) type ExtractKeyFromArg<Arg, T> = Arg extends `/${string}` ? Arg : keyof Arg extends keyof T ? keyof Arg : never; // 递归函数类型 type Foo<T extends object> = <Arg>( args: Arg extends Argument<infer K> ? K extends keyof T ? Arg : never : never ) => T[ExtractKeyFromArg<Arg, T>] extends object ? Foo<T[ExtractKeyFromArg<Arg, T>]> : T[ExtractKeyFromArg<Arg, T>];
测试验证
const values = { '/aaa': { '/lorem': { foo: 'hello world', '/boo': 12345, }, }, bbb: { '/ipsum': { '/dolor': 'lorem ipsum', amet: 567890, }, }, }; const foo = {} as Foo<typeof values>; // 正常工作,返回number类型 foo('/aaa')('/lorem')('/boo'); // 正常工作,返回string类型 foo('/aaa')('/lorem')({ foo: '...' }); // 报错:类型"/ipsum"的参数不能赋给类型"/lorem" | { foo: string | number; }的参数 foo('/aaa')('/ipsum'); // 正常工作,返回number类型 foo({ bbb: '...' })('/ipsum')({ amet: '...' }); // 正常工作,返回string类型 foo({ bbb: '...' })('/ipsum')('/dolor'); // 报错:类型"/lorem"的参数不能赋给类型"/ipsum"的参数 foo({ bbb: '...' })('/lorem');
关键逻辑说明
ExtractKeyFromArg类型:- 如果参数是
/${string}类型的键,直接返回该键; - 如果参数是对象,提取对象的键并验证是否属于当前层级的键集合,确保参数合法性。
- 如果参数是
Foo递归类型:- 使用条件类型推断(
infer K)从参数中提取对应的键K,并验证K是否属于当前对象T的键; - 根据
T[K]的类型决定返回下一层递归函数Foo<T[K]>,还是直接返回值类型T[K]。
- 使用条件类型推断(
这个方案完美适配了你需求中的两种参数形式,同时保留了类型校验的严格性,不符合层级的参数会直接触发类型报错。
内容的提问来源于stack exchange,提问作者Stephan D.
相关产品推荐
相关产品推荐

