digamos que tengo una lista de objetos, y cada objeto es un tipo de IUser
type IUser = { id: string name: string ... }Quiero convertir esta lista en un mapa, usar id como clave (u otra propiedad del objeto).
Y espero poder tener una descripción mecanografiada precisa para este tipo, para poder usarlo así const idMap :IMapKeyedByK<IUser, 'id'> = {...}
type IMapKeyedByK<T, K> = { [key: T[K]]: T }¿Cómo puedo implementar IMapKeyedByK<T, K> ?
Tenga en cuenta que los tipos de índice de objetos de JavaScript siempre son cadenas o símbolos. Puede indexar un objeto usando números, pero se convierten en cadenas: ({42: 'foo'})['42'] == 'foo' . Entonces, a menos que su IUser contenga symbol s, esto no es muy útil porque también podría codificar claves string .
Dicho esto, puedes hacerlo. Tenemos que restringir K para que sea un tipo de clave válido y restringir T para que contenga una clave de tipo K con un valor que también se pueda usar como clave.
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.Un tipo correcto aquí es:
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; Se asegurará de que pase una clave que pertenezca a T y también de que el valor de esa clave sea válido para usar como clave de objeto.
Ejemplo:
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