TypeScript中如何从父对象获取属性作为Map键,避免重复定义?
TypeScript 属性关联问题解决方案
原类型与对象定义
type IProperty<Type> = { [index: string]: Type } & { name: string } const testProp: IProperty<string> = { name: 'credential', valid: 'ABC', invalid: '123', }
期望实现的逻辑
class Actor { private props = new Map<string, unknown>() give(prop: unknown) { this.props.set(prop.name, prop) // 此处无法获取prop.name } get(prop: IProperty<unknown>): unknown { const value = this.props.get(prop.name) if (value !== undefined) return value else throw new Error(`Actor does not have ${prop.name}`) } } const actor = new Actor() actor.give(testProp.valid) console.log(actor.get(testProp)) // 期望输出'ABC' actor.give(testProp.invalid) console.log(actor.get(testProp)) // 期望输出'123'
核心问题
调用actor.give()时传入的testProp.valid这类子属性本身不包含name,该属性仅存在于父对象testProp中。需要在不重复定义name的前提下,让Actor的give方法能拿到父对象的name作为Map的键。
解决方案一:映射类型包装属性绑定父对象name
通过映射类型将父对象的name与子属性值绑定,同时保留原属性的访问方式:
1. 定义关联类型
// 封装父name与属性值的结构 type PropWithParentName<T> = { parentName: string; value: T; }; // 将IProperty转换为每个子属性都带parentName的类型 type MappedProperty<T> = { [K in keyof Omit<T, 'name'>]: PropWithParentName<T[K]>; } & { name: T['name'] };
2. 创建工具函数生成绑定对象
function createProperty<T extends { name: string }>(base: T): MappedProperty<T> { const result = { name: base.name } as MappedProperty<T>; // 遍历非name属性,包装成带parentName的结构 for (const key in base) { if (key !== 'name') { (result as any)[key] = { parentName: base.name, value: base[key] }; } } return result; }
3. 调整Actor类适配新类型
class Actor { private props = new Map<string, unknown>() give(prop: PropWithParentName<unknown>) { this.props.set(prop.parentName, prop.value) } get(prop: { name: string }): unknown { const value = this.props.get(prop.name) if (value !== undefined) return value else throw new Error(`Actor does not have ${prop.name}`) } }
4. 使用示例
const testProp = createProperty({ name: 'credential', valid: 'ABC', invalid: '123', }); const actor = new Actor() actor.give(testProp.valid) console.log(actor.get(testProp)) // 输出'ABC' actor.give(testProp.invalid) console.log(actor.get(testProp)) // 输出'123'
解决方案二:传递父对象+属性键(更简洁)
无需包装类型,直接通过传递父对象和属性键来获取name:
class Actor { private props = new Map<string, unknown>() give<T extends IProperty<any>, K extends keyof Omit<T, 'name'>>(propObj: T, key: K) { this.props.set(propObj.name, propObj[key]) } get(prop: IProperty<unknown>): unknown { const value = this.props.get(prop.name) if (value !== undefined) return value else throw new Error(`Actor does not have ${prop.name}`) } } // 使用示例 const actor = new Actor() actor.give(testProp, 'valid') console.log(actor.get(testProp)) // 输出'ABC' actor.give(testProp, 'invalid') console.log(actor.get(testProp)) // 输出'123'
内容的提问来源于stack exchange,提问作者Matt Mohandiss
相关产品推荐
相关产品推荐

