TypeScript:为对象数组转属性值为键的结构定义正确类型
嘿,这个需求我熟!把对象数组转成以指定属性值为键、原对象为值的结构,TypeScript完全可以通过泛型类型定义实现类型安全的转换,下面是一套实用的方案:
核心泛型类型定义
我们可以先定义一个泛型类型RecordFromArray,它能根据输入的数组类型和指定的键属性,自动推导目标对象的类型:
type RecordFromArray< T extends readonly { [K in Key]: PropertyKey }[], Key extends keyof T[number] > = { // 用as子句重映射键:取每个数组元素的Key属性值作为新对象的键 [Item in T[number] as Item[Key]]: Item; };
类型解释
T extends readonly { [K in Key]: PropertyKey }[]:约束输入的数组元素必须包含Key这个属性,且属性值是有效的键类型(PropertyKey是TypeScript内置的string | number | symbol别名,确保能作为对象键)[Item in T[number] as Item[Key]]:这是TypeScript的键重映射语法,遍历数组的每个元素类型,把元素的Key属性值作为新对象的键,值则保留原元素的类型。
实际使用示例
我们用一个用户数组的例子来演示:
// 先定义基础对象类型 type User = { id: string; name: string; age: number }; // 示例数组(用const断言可以让TypeScript推导更精确的键值) const users = [ { id: "user1", name: "Alice", age: 30 }, { id: "user2", name: "Bob", age: 25 }, ] as const; // 生成以id为键的对象类型 type UsersById = RecordFromArray<typeof users, "id">;
此时UsersById的类型会被精确推导为:
{ user1: { readonly id: "user1"; readonly name: "Alice"; readonly age: 30 }; user2: { readonly id: "user2"; readonly name: "Bob"; readonly age: 25 }; }
带类型约束的转换函数
如果需要实际的转换函数,我们可以把上面的泛型类型和函数结合,实现完全类型安全的转换:
function arrayToRecord< T extends readonly { [K in Key]: PropertyKey }[], Key extends keyof T[number] >(array: T, key: Key): RecordFromArray<T, Key> { return array.reduce((acc, item) => { acc[item[key] as keyof typeof acc] = item; return acc; }, {} as RecordFromArray<T, Key>); } // 使用函数转换 const usersById = arrayToRecord(users, "id"); // usersById 的类型完全匹配UsersById,访问usersById.user1时会有完整的类型提示
注意事项
- 如果数组中存在重复的键值,TypeScript会自动合并对应的值类型(比如两个元素的id都是"user1",但属性值不同,值类型会变成联合类型)
- 类型约束会确保你选择的键属性一定存在于数组元素中,且属性值是合法的对象键,避免运行时出现无效键的问题
内容的提问来源于stack exchange,提问作者Tomáš Burda
相关产品推荐
相关产品推荐

