Reputation: 95
I want to take a constant object, say {key1: 'value1', key2: value2'} as const
, and convert its values into keys and use it in a new object with type {value1: number; value2: number}
. I'd like to do this in a function, and I want the function to be typed correctly.
Below is the code that I'm trying to get to work. The error is happening when I try to cast a variable of type Partial<Blah>
to Blah
. Could someone explain why it's happening, and if there's a way to do what I'm trying to do? Thanks!
type BaseObject = Record<string, string>;
type ValuesAsKeys<T extends BaseObject> = {
[K in keyof T as T[K]]: number;
};
function useValuesAsKeys<T extends BaseObject>(arg: T): ValuesAsKeys<T> {
const x: Partial<ValuesAsKeys<T>> = {};
for (const value of Object.values(arg) as Array<T[keyof T]>) {
x[value] = 1;
}
// This line doesn't work
return x as ValuesAsKeys<T>;
}
// Example use case
const obj = {key1: 'value1', key2: 'value2'} as const;
const result = useValuesAsKeys(obj); // Type of result is {value1: number; value2: number}
// Everything below here works
// Only including this because I'm contrasting this code with the code above.
type KeysAsKeys<T extends BaseObject> = {
[K in keyof T]: number;
};
function useKeysAsKeys<T extends BaseObject>(arg: T): KeysAsKeys<T> {
const x: Partial<KeysAsKeys<T>> = {};
for (const value of Object.values(arg) as Array<T[keyof T]>) {
x[value] = 1;
}
return x as KeysAsKeys<T>;
}
Upvotes: 5
Views: 9850
Reputation: 41
There is a simpler way to accomplish that.
const obj = { key1: 'value1', key2: 'value2' } as const;
type ValuesAsKeys = typeof obj[keyof typeof obj];
const myUseOfObject: { [key in ValuesAsKeys]: number } = {
value1: 1,
value2: 2
};
Upvotes: 4
Reputation: 95
I'm still not sure why casting Partial<Blah>
to Blah
doesn't work in this scenario, but this code works and is type-safe enough for me:
function useValuesAsKeys<T extends BaseObject>(arg: T): ValuesAsKeys<T> {
const x = {} as ValuesAsKeys<T>;
for (const value of Object.values(arg) as Array<T[keyof T]>) {
x[value] = 1;
}
return x;
}
Upvotes: 0
Reputation: 33091
Explicit return type with reduce
almost always does not work, because initial
values changes every iteration.
In order to make it work, it is better to overload your function:
type BaseObject = Record<string, string>;
type ValuesAsKeys<T extends BaseObject> = {
[K in keyof T as T[K]]: number;
};
type Values<T> = T[keyof T]
function useValuesAsKeys<T extends BaseObject>(arg: T): ValuesAsKeys<T>
function useValuesAsKeys<T extends BaseObject>(arg: T) {
return (Object.values(arg) as Array<Values<T>>).reduce((acc, elem) => ({
...acc,
[elem]: 1
}), {})
}
// Example use case
const obj = { key1: 'value1', key2: 'value2' } as const;
const result = useValuesAsKeys(obj); // Type of result is {value1: number; value2: number}
result.value1 //ok
Upvotes: 1