TypeScript如何实现配置对象子属性oneOfKeys为父对象键名的类型约束
解决方案
核心思路是调整泛型推断优先级,让TS优先从配置对象的顶层键推断键名集合,再用这个集合约束所有oneOfKeys的取值。
可直接运行的代码实现
TS 4.9及以上版本推荐加const修饰符,避免字面量类型意外收窄:
// 泛型先接收完整配置对象类型,再自动约束内部oneOfKeys的取值范围 function testFunc<const T extends Record<string, { oneOfKeys: keyof T }>>(foo: T) {}
低版本TS去掉const修饰符即可:
function testFunc<T extends Record<string, { oneOfKeys: keyof T }>>(foo: T) {}
如果需要把父子类型单独抽离定义,可以写成:
interface ChildType<KeyNames extends string> { oneOfKeys : KeyNames } type ParentType<T extends Record<string, any>> = { [K in keyof T]: ChildType<keyof T>; }; function testFunc<const T extends ParentType<T>>(foo: T) {}
效果说明
用你提供的测试用例运行时,校验逻辑会完全符合预期:
- 顶层的
bal键不会再抛出错误,TS会正确推断顶层键集合为'foo' | 'bar' | 'bal' bar属性的oneOfKeys: "baz"会抛出错误,因为baz不属于顶层键的集合
原写法问题原因
原来的写法中泛型参数KeyNames同时被顶层键、oneOfKeys两个位置作为类型来源,TS的泛型推断会收集所有位置的候选值合并为最终的泛型类型,不会优先采用顶层键的集合,所以oneOfKeys里的baz会被加入KeyNames的候选值,最终推断出的KeyNames是'foo' | 'bar' | 'baz',而顶层键bal不在这个集合里,就会抛出错误,和需求完全相反。
内容的提问来源于stack exchange,提问作者Vincent J
相关产品推荐
相关产品推荐

