Not sure why typescript is complaining here
type ObjectWithMatchingProperties<T> = {
[K in keyof T]: T[K];
};
type MainState = {
selectedProduction: {
eventName: string
}
activePricingGroup: {
name: string
}
}
type MainStateObj = ObjectWithMatchingProperties<MainState>
type InventoryState = {
mainSlice: MainStateObj;
};
const state: InventoryState = {
mainSlice: {
selectedProduction: {
eventName: 'test',
},
activePricingGroup: {
name: 'act',
}
}};
export const useMainInventoryStore = (key: keyof MainState) => state.mainSlice[key]
const selectedProduction = useMainInventoryStore('selectedProduction');
// error at this line:
selectedProduction.eventName
The code Errors out with
Property 'eventName' does not exist on type '(state: InventoryState) => { eventName: string; } | { name: string; }'.
Here is the playgroud
Your useMainInventoryStore
function's return type does not depend on its input. Since the input is the union type keyof MainState
, then the output is the corresponding union type { eventName: string; } | { name: string; }
. Even if you call it with "selectedProduction"
as the input, the output is a union type, and TypeScript has no idea whether it will have an eventName
property or a name
property. And so it complains if you try to index into it with either key (see Typescript property does not exist on union type).
If you'd like the function's output type to depend on the input type, you should make it a generic function, like this:
const useMainInventoryStore =
<K extends keyof MainState>(key: K) => state.mainSlice[key]
Now the input is of generic type K
which is constrained to keyof MainState
. And thus the output is of the generic type ObjectWithMatchingProperties<MainState>[K]
, a generic indexed access type corresponding to whatever property exists at state.mainSlice[key]
.
And now when you call the function the output type reflects the type of the input:
const selectedProduction = useMainInventoryStore('selectedProduction')
// const selectedProduction: {eventName: string;}
And you can index into it:
selectedProduction.eventName // okay