You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:27:47