如何实现基于Attribute类型name字段的类型安全对象映射?
问题
现有一个带name属性的泛型类型:
type Attribute<Name, Value> = { name : Name value : Value }
关键说明:
name参数必须是精确的字符串字面量,比如用Attribute<"width", number>而非Attribute<string, number>;- 实际场景中
Attribute包含更多属性,这里只保留value简化示例。
需要创建一个映射对象类型,将Attribute["name"]映射到对应的Attribute类型。比如包含Attribute<"width", number>和Attribute<"height", number>的映射对象m,访问m.width时应返回Attribute<"width", number>类型,访问m.height时返回Attribute<"height", number>类型,示例如下:
function f(m : AttributesMap<[ Attribute<"width", number>, Attribute<"height", number>, ]>) { m.width.name // 类型应为"width" m.height.name // 类型应为"height" }
尝试了以下写法,但访问m.width和m.height时,返回的都是Attribute<"width", number> | Attribute<"height", number>联合类型:
type AttributesMap< Attribute_ extends Attribute<string, any>[] > = { [key in Attribute_[number][`name`]] : Attribute_[number] }
已知问题出在左右两侧的[number]相互独立,但不知道如何正确实现需求,寻求解决方案。
解决方案
要实现精确的映射,需要利用映射类型结合精确类型提取,核心是让每个键对应到数组中name属性匹配该键的具体Attribute类型。
正确的类型定义如下:
type AttributesMap<T extends Attribute<string, any>[]> = { [K in T[number]['name']]: Extract<T[number], { name: K }> }
原理说明
T[number]['name']:提取数组中所有Attribute的name字面量,作为映射的键集合;Extract<T[number], { name: K }>:从数组元素的联合类型中,精确筛选出name属性等于当前键K的那个Attribute类型,确保每个键对应唯一的具体类型,而非整个联合类型。
验证示例
用测试函数验证类型正确性:
function f(m : AttributesMap<[ Attribute<"width", number>, Attribute<"height", number>, ]>) { m.width.name // 类型为"width" ✅ m.height.name // 类型为"height" ✅ // 访问不存在的键(如m.foo)会直接触发类型错误 }
如果需要支持更灵活的输入(比如直接传入联合类型而非数组),可以调整泛型参数:
type AttributesMap<T extends Attribute<string, any>> = { [K in T['name']]: Extract<T, { name: K }> } // 使用方式变为传入联合类型 function f(m : AttributesMap< Attribute<"width", number> | Attribute<"height", number> >) { m.width.name // "width" m.height.name // "height" }
内容的提问来源于stack exchange,提问作者hopeless-programmer
相关产品推荐
相关产品推荐

