TypeScript中如何为自引用索引存储配置对象定义类型约束?
解决方案
你的需求完全可以在TypeScript中实现,核心是用泛型推导辅助函数规避显式自引用的问题,不需要拆分配置声明,完全保留原有配置的简洁性。
完整实现代码
// 工具类型:提取单个索引的输出类型 type IndexOutput<T> = T extends (...args: any[]) => infer R ? R : T extends Record<string, (...args: any[]) => infer R> ? R : never; // 工具类型:提取所有索引的输出类型映射 type AllIndexOutputs<T extends object, InitialIn> = { [K in keyof T]: IndexOutput<T[K]> }; // 工具类型:约束索引配置的结构 type IndexConfig<InitialIn, T extends object> = { [K in keyof T]: // 主索引:函数,入参是初始输入类型 | ((value: InitialIn) => any) // 衍生索引:对象,键必须是已存在的索引名,函数入参是对应索引的输出类型 | { [FromK in keyof AllIndexOutputs<T, InitialIn>]: ( value: AllIndexOutputs<T, InitialIn>[FromK] ) => any } }; // 辅助构造函数:只需要传入初始输入类型,剩余泛型自动推导 function defineIndexConfig<InitialIn>() { return function<T extends object>(config: IndexConfig<InitialIn, T>) { return config as const; } }
使用示例
合法配置(无编译错误)
和你原来的写法几乎完全一致,只需要套一层辅助函数:
const chainedMap = defineIndexConfig<string>()({ length: (value: string) => value.length, threshold: { length: (value: number) => value >= 10, }, serialise: { length: (value: number) => value.toString(), threshold: (value: boolean) => value.toString(), }, });
非法配置(自动抛出编译错误)
你的错误示例会准确抛出两个预期错误:
const badChainedMap = defineIndexConfig<string>()({ length: (value: string) => value.length, threshold: { length: (value: number) => value >= 10, // 错误:对象字面量只能指定已知属性,类型中不存在“foo” foo: (value: boolean) => !value, }, serialise: { length: (value: number) => value.toString(), // 错误:参数“value”的类型为number,无法分配给类型boolean的参数 threshold: (value: number) => value.toString(), }, });
问题解答
这种自引用类型是否可以在TypeScript中实现?
可以实现。你之前遇到的自引用报错是因为显式把typeof chainedMap作为泛型参数传入类型定义,而通过辅助函数的泛型自动推导,TypeScript可以隐式完成同对象内的递归类型校验,不需要显式声明自引用类型。有没有替代模式可以在保留配置对象简洁声明的前提下,校验配置中函数的逻辑完整性?
上面提供的辅助构造函数模式就是最优方案:完全不需要修改你原有配置的结构和写法,只需要在配置外套一层函数调用,就可以获得全量的类型校验能力,不需要拆分任何声明。
内容的提问来源于stack exchange,提问作者cefn
相关产品推荐
相关产品推荐

