如何实现TypeScript泛型接口的动态属性值类型关联?
解决TypeScript中动态关联
name与fields类型的问题 原代码中Select<T>接口无法使用this.name作为类型索引,因为TypeScript的静态类型系统不支持在接口定义中依赖实例的动态属性值。要实现name指定的键与fields允许的子键类型关联,有两种可行方案:
方案一:泛型参数绑定具体键类型
通过新增泛型参数K明确name的具体类型,让fields的类型与K对应的子属性绑定:
interface Building { rooms: number kitchen: { hasOven: true } bathroom: { numberOfSinks: number } } // 泛型K约束为T的键,fields仅允许T[K]的子键(仅当T[K]是对象时) type Select<T, K extends keyof T> = { name: K fields: T[K] extends object ? (keyof T[K])[] : never } // 手动指定泛型类型 const x: Select<Building, 'kitchen'> = { name: 'kitchen', fields: ['hasOven'] } const y: Select<Building, 'bathroom'> = { name: 'bathroom', fields: ['numberOfSinks'] } // 辅助函数自动推断泛型,无需手动指定 function createSelect<T, K extends keyof T>(select: Select<T, K>) { return select } const z = createSelect({ name: 'bathroom', fields: ['numberOfSinks'] } as const)
这种方案适合需要明确控制name类型的场景,同时通过T[K] extends object的条件过滤掉非对象类型的属性(比如rooms),避免非法赋值。
方案二:自动生成合法组合的联合类型
利用映射类型和联合类型,让Select<T>自动包含所有name与对应fields的合法组合:
interface Building { rooms: number kitchen: { hasOven: true } bathroom: { numberOfSinks: number } } // 遍历T的所有键,生成对应的合法结构,再组合为联合类型 type Select<T> = { [K in keyof T]: T[K] extends object ? { name: K fields: (keyof T[K])[] } : never }[keyof T] // 直接使用,TS会自动校验name与fields的对应关系 const x: Select<Building> = { name: 'kitchen', fields: ['hasOven'] } const y: Select<Building> = { name: 'bathroom', fields: ['numberOfSinks'] } // 以下代码会报错:rooms对应的类型是never,不允许赋值 // const invalid: Select<Building> = { // name: 'rooms', // fields: [] // }
这种方案无需手动指定泛型,TS会自动推断所有合法的name和fields组合,更适合需要灵活创建实例的场景。
内容的提问来源于stack exchange,提问作者Kai Sellgren
相关产品推荐
相关产品推荐

