是否可以将某些键的值用作打字稿中的新键?
is it possible to use value of some key as new key in typescript?
假设我有一个对象列表,每个对象都是 IUser 类型
type IUser = {
id: string
name: string
...
}
我想将此列表转换为地图,使用 id 作为键(或来自对象的其他 属性)。
希望我能有一个准确的类型描述,这样我就可以像这样使用它const idMap :IMapKeyedByK<IUser, 'id'> = {...}
type IMapKeyedByK<T, K> = {
[key: T[K]]: T
}
如何实现 IMapKeyedByK ?
请注意,JavaScript 对象索引类型始终是字符串或符号。您可以使用数字索引对象,但它们会转换为字符串:({42: 'foo'})['42'] == 'foo'
。因此,除非您的 IUser
包含 symbol
s,否则这不是很有用,因为您最好对 string
键进行硬编码。
也就是说,你可以做到。我们必须将 K
约束为有效的键类型,并约束 T
以便它包含类型 K
的键,其值可以 也 用作键。
type ObjectKey = string | number | symbol
type IMapKeyedByK<T extends Record<K, ObjectKey>, K extends ObjectKey> = Record<T[K], T>
const idMap: IMapKeyedByK<IUser, 'id'> = {}
idMap['zaphod'] = {id: 'zaphod', name: 'Zaphod Beeblebrox'} // OK
idMap[Symbol()] = {id: 'zaphod', name: 'Zaphod Beeblebrox'} // Error! Type 'symbol' cannot be used as an index type.
这里的正确类型是:
type IMapKeyedByK<T, K extends keyof T> = T[K] extends PropertyKey // if the property value can be used as key
? Record<T[K], T> // record indexed by the property value
: never;
它将确保您传递属于 T
的密钥,并且该密钥的值可有效用作对象密钥。
示例:
type IUser = {
id: string
name: string
age: number
department: {
departmentName: string
}
}
type IMapKeyedByK<T, K extends keyof T> = T[K] extends PropertyKey ? Record<T[K], T> : never;
type MapById = IMapKeyedByK<IUser, "id">; //works - indexed by string
type MapByName = IMapKeyedByK<IUser, "name">; //works - indexed by string
type MapByAge = IMapKeyedByK<IUser, "age">; //works - indexed by number
type MapByDepartment = IMapKeyedByK<IUser, "department">; //produces `never` because cannot be indexed by object
type MapByPhone = IMapKeyedByK<IUser, "phone">; //error - "phone" does not exist
假设我有一个对象列表,每个对象都是 IUser 类型
type IUser = {
id: string
name: string
...
}
我想将此列表转换为地图,使用 id 作为键(或来自对象的其他 属性)。
希望我能有一个准确的类型描述,这样我就可以像这样使用它const idMap :IMapKeyedByK<IUser, 'id'> = {...}
type IMapKeyedByK<T, K> = {
[key: T[K]]: T
}
如何实现 IMapKeyedByK
请注意,JavaScript 对象索引类型始终是字符串或符号。您可以使用数字索引对象,但它们会转换为字符串:({42: 'foo'})['42'] == 'foo'
。因此,除非您的 IUser
包含 symbol
s,否则这不是很有用,因为您最好对 string
键进行硬编码。
也就是说,你可以做到。我们必须将 K
约束为有效的键类型,并约束 T
以便它包含类型 K
的键,其值可以 也 用作键。
type ObjectKey = string | number | symbol
type IMapKeyedByK<T extends Record<K, ObjectKey>, K extends ObjectKey> = Record<T[K], T>
const idMap: IMapKeyedByK<IUser, 'id'> = {}
idMap['zaphod'] = {id: 'zaphod', name: 'Zaphod Beeblebrox'} // OK
idMap[Symbol()] = {id: 'zaphod', name: 'Zaphod Beeblebrox'} // Error! Type 'symbol' cannot be used as an index type.
这里的正确类型是:
type IMapKeyedByK<T, K extends keyof T> = T[K] extends PropertyKey // if the property value can be used as key
? Record<T[K], T> // record indexed by the property value
: never;
它将确保您传递属于 T
的密钥,并且该密钥的值可有效用作对象密钥。
示例:
type IUser = {
id: string
name: string
age: number
department: {
departmentName: string
}
}
type IMapKeyedByK<T, K extends keyof T> = T[K] extends PropertyKey ? Record<T[K], T> : never;
type MapById = IMapKeyedByK<IUser, "id">; //works - indexed by string
type MapByName = IMapKeyedByK<IUser, "name">; //works - indexed by string
type MapByAge = IMapKeyedByK<IUser, "age">; //works - indexed by number
type MapByDepartment = IMapKeyedByK<IUser, "department">; //produces `never` because cannot be indexed by object
type MapByPhone = IMapKeyedByK<IUser, "phone">; //error - "phone" does not exist