如何将含指定对象的数组转换为TypeScript严格类型对象?
TypeScript数组转严格类型对象的类型实现问题
需求说明
我需要编写一个TypeScript类型,将包含name(字符串类型)和泛型value的对象数组,转换为键值严格对应的对象。示例如下:
// MyObj包含name和value属性 const arr = [ new MyObj("a", 1), new MyObj("b", "two"), new MyObj("c", true), ]; const map: TypeToConvertArrayIntoMap<typeof arr> = {} as any; map.a; // 期望类型为1或number均可 map.b; // 期望类型为"two"或string均可 map.c; // 期望类型为true或boolean均可
当前问题
我尝试编写的类型返回了所有值的联合类型(比如1 | "two" | true),而非每个键对应各自的类型。我的代码如下:
/** 数组元素的类型 */ type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer E)[] ? E : never; type PossibleValues = number | string | boolean; class MyObj<Name extends string, V extends PossibleValues> { constructor(public name: Name, public value: V) {} } type MyMap<TObjArr extends MyObj<string, PossibleValues>[]> = { [key in ElementOf<TObjArr>["name"]]: ElementOf<TObjArr>["value"]; }; const arr = [ new MyObj("a", 1), new MyObj("b", "two"), new MyObj("c", true), ]; const map: MyMap<typeof arr> = {} as any; map.a; // 实际类型:true | 1 | "two",不符合期望 map.b; // 实际类型:true | 1 | "two",不符合期望 map.c; // 实际类型:true | 1 | "two",不符合期望
解决方案
问题出在ElementOf会提取数组元素的联合类型,导致所有键都映射到所有值的联合。要实现严格的键值对应,需要遍历数组的每个元素,将每个元素的name作为键,对应value作为值。
正确的类型实现
type PossibleValues = number | string | boolean; class MyObj<Name extends string, V extends PossibleValues> { constructor(public name: Name, public value: V) {} } // 核心类型:遍历数组的每个元素,建立键值映射 type ArrayToMap<T extends readonly MyObj<string, PossibleValues>[]> = { [K in T[number]["name"]]: Extract<T[number], { name: K }>["value"]; }; // 用as const让数组元素的类型被精确推断 const arr = [ new MyObj("a", 1), new MyObj("b", "two"), new MyObj("c", true), ] as const; const map: ArrayToMap<typeof arr> = {} as any; map.a; // 类型:1 map.b; // 类型:"two" map.c; // 类型:true
关键说明
- 使用
T[number]替代自定义的ElementOf,直接获取数组元素的联合类型; - 通过
Extract<T[number], { name: K }>从联合类型中筛选出name为当前键K的元素,从而拿到对应的value类型; - 必须给数组添加
as const断言,让TypeScript精确推断每个数组元素的具体类型(而非宽泛的MyObj<string, PossibleValues>),否则无法建立精准的键值映射。
如果希望值的类型是宽泛的基础类型(比如number而非1),可以修改类型如下:
type ArrayToMap<T extends readonly MyObj<string, PossibleValues>[]> = { [K in T[number]["name"]]: Extract<T[number], { name: K }>["value"] extends infer V ? V extends number ? number : V extends string ? string : V extends boolean ? boolean : never : never; }; // 此时map.a的类型为number,map.b为string,map.c为boolean
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

