Typescript中如何将对象数组转换为指定键索引的对象集合
你写的代码报错的核心原因是没有约束指定key对应的属性值类型,只有string | number | symbol类型的值可以作为对象的键,当你直接用item[key]作为对象索引时,TS无法确定T[key]是合法的键类型,因此抛出类型错误。
修正后的完整实现
// PropertyKey是TS内置类型,等价于 string | number | symbol,即所有合法对象键的联合类型 const convert = <T extends Record<K, PropertyKey>, K extends keyof T>(arr: T[], key: K) => { // 定义返回对象类型为:键是传入key对应的值类型,值是数组的元素类型 const result = {} as Record<T[K], T> arr.forEach(item => { result[item[key]] = item }) return result }
改动说明
- 新增泛型参数
K,用来约束传入的key必须是数组元素T的合法属性 - 通过
T extends Record<K, PropertyKey>约束T中K属性的值必须是合法的对象键类型,如果你传入的key对应的值是对象、数组等不能作为键的类型,TS会在调用阶段直接提示错误 - 返回值使用
Record<T[K], T>类型,比你原先的Collection<T>类型更精准:比如你示例中传入name作为key,返回对象的键会被自动推导为"alice" | "bob"的字面量联合类型,后续使用时能获得更完善的类型提示,避免非法键访问的错误。
使用示例
const myArr = [ { name: "alice", id: "54dc2a", age: 23 }, { name: "bob", id: "662adf", age: 60 } ] // 传入name作为key const nameMap = convert(myArr, "name") // 自动推导nameMap类型为 Record<"alice"|"bob", {name: string; id: string; age: number}> console.log(nameMap.alice) // 正常提示,无报错 console.log(nameMap.nobody) // TS会直接提示该键不存在,提前发现错误 // 传入id作为key也可以正常使用 const idMap = convert(myArr, "id") // idMap类型推导为 Record<"54dc2a"|"662adf", typeof myArr[number]>
如果你需要保留之前定义的Collection类型,可以修改为如下形式兼容:
type Collection<K extends PropertyKey, T> = { [key in K]: T } // 对应的convert实现 const convert = <T extends Record<K, PropertyKey>, K extends keyof T>(arr: T[], key: K): Collection<T[K], T> => { const result = {} as Collection<T[K], T> arr.forEach(item => { result[item[key]] = item }) return result }
内容的提问来源于stack exchange,提问作者Lars
相关产品推荐
相关产品推荐

