I have an array of items, each with a unique type
string property. I want to convert this into a type where the type is the key and the value is the original item.
The first thing I do is to ensure I have a correctly typed tuple
type MyObject = { type: string; [key: string]: any }
const createObjectList = <T extends ReadonlyArray<MyObject>>(items: T) => items;
const objectList = createObjectList([
{ type: 'a', value: 'foo' },
{ type: 'b', value: 10 },
] as const)
// objectList[0].value is corectly infered as 'foo'
// objectList[1].value is corectly infered as 10
After that, I try to convert it into the object type.
type ObjectDictionary = {
[key in typeof objectList[number]['type']]: typeof objectList[number];
}
const typed: ObjectDictionary = undefined as any;
The issue here is that, of course, the value is now a union type off all possible values instead of the one specific to that key, so the result is equal to
type ObjectDictionary = {
a: { value: string | number };
b: { value: string | number };
}
instead of
type ObjectDictionary = {
a: { value: string };
b: { value: number };
}
Any idea how to get the individual item type in ObjectDictionary
?
node: this isn't the actual code that I am working with; I just tried to simply the core issue
CodePudding user response:
The reason you are getting a union is of course because you are using typeof objectList[number]
as the type of the property, here is no relation to type
.
If you are using an older version of TS you can use Extract
to get only the item type that as the same type as the current key. Playground Link
You can also use the as
clause in mapped types (since TS 4.1, PR) to map over the union of item type in the array. As the key you can select the type
in the as
clause and then you have the corresponding item type in the mapping type parameter:
type MyObject = { type: string; [key: string]: any }
const createObjectList = <T extends ReadonlyArray<MyObject>>(items: T) => items;
const objectList = createObjectList([
{ type: 'a', value: 'foo' },
{ type: 'b', value: 10 },
] as const)
type ObjectDictionary = {
[TItem in typeof objectList[number] as item['type']]: Omit<TItem, 'type>'>;
}
const typed: ObjectDictionary = undefined as any;