I have an object type which I want to map to a strongly typed key, value array.
This is what I came up with so far:
export interface StepWidgetItem<T, V> {
key: T;
value: V;
}
interface UseStepWidgetsContainer<T extends object> {
data: T;
}
interface UseStepWidgetsContainerProps<T extends object> {
items: StepWidgetItem<keyof T, T[keyof T]>[];
}
export const useStepWidgetsContainer = <T extends object>({
items,
}: UseStepWidgetsContainerProps<T>): UseStepWidgetsContainer<T> => {
const data = {} as T;
console.log(items);
return {
data,
};
};
The problem i when I call it like this:
interface Data {
test: string;
test2: number;
}
useStepWidgetsContainer<Data>({
items: [
{
key: 'test',
value: 'test',
},
{
key: 'test2',
value: 'test', // should be an error, but string is currently allowed...
},
{
key: 'test2',
value: 1,
},
],
});
The value of array item with key: 'test2' must be a number, but currently it may be any type of the values of Data, so string|number
.
Is it possible to force a number when key === 'test2'?
CodePudding user response:
You are very close The problem is that StepWidgetItem<keyof T, T[keyof T]>
will mix any key of T
with any value in T
.
The solution is to first associate the key with the value creating a type for each property, for ex: StepWidgetItem<"test", string>
StepWidgetItem<"test2", number>
. We can do this using a mapped type to first create for each property, the associated StepWidgetItem
and then index into the mapped type using [keyof T]
to get a union of all StepWidgetItem types we just created.
interface UseStepWidgetsContainerProps<T extends object> {
items: Array<{
[P in keyof T]: StepWidgetItem<P, T[P]>
}[keyof T]>
}