You can use an indexed access type combined with keyof
to get what you want.
Code
type ValuesAsKeys<T extends Record<any, PropertyKey>, NewValue> = Record<T[keyof T], NewValue>
export const CODES = {
ALPHA: 'alpha',
OMEGA: 'omega',
} as const;
export type CODES_OBJECTS = ValuesAsKeys<typeof CODES, {}>
/* resolves to type CODES_OBJECTS = {
alpha: {};
omega: {};
} */
Playground Link
Explanation
The type ValuesAsKeys
takes two generics -- T
is the object whose values we want to use as keys (your CODES
const) and NewValue
is the value that we want to assign to those keys (here it's an empty object {}
but you probably want something better).
In order to use the values of T
as keys, we have to insist that the values of T
are something that can be used as a key. We do that with T extends Record<any, PropertyKey>
which uses the built-in type PropertyKey
.
We can access the union of all values of T
with T[keyof T]
which is an indexed access type. We want to create a Record
where those are the new keys. So we get Record<T[keyof T], NewValue>
.
In order for typescript to see alpha
and omega
as their literal values and not just string
, we use as const
when creating CODES
.
CODES
is a value rather than a type, so we use typeof CODES
as T
.
Enum Version
The typescript is almost identical if using an enum
. The only difference is that you don't need to add as const
.
type ValuesAsKeys<T extends Record<any, PropertyKey>, NewValue> = Record<T[keyof T], NewValue>
enum CODES {
ALPHA = 'alpha',
OMEGA = 'omega',
}
export type CODES_OBJECTS = ValuesAsKeys<typeof CODES, {}>
Playground Link