ReturnType<typeof setBookProperty>
is only evaluated once to determine the type for
action
. However, this type does not maintain any relationship between
propertyName
and
propertyValue
. It simply represents the union of all possible values for each without considering their interdependency.
When examining the type of action
, you will observe:
{
payload: {
propertyName: "title" | "chapters";
propertyValue: string | string[];
};
}
This lack of correlation means that narrowing down one value does not affect the other.
To establish a connection between the two, your action type must encompass all valid pairings. This approach involves writing additional code but ensures the desired outcome.
Create a generic action type that specifies the pairing for a particular key:
type ActionSpecific<K extends keyof Book> = {
payload: {
propertyName: K,
propertyValue: Book[K]
}
}
Utilize a mapping technique to generate the union for all keys. In this scenario, since there are only two keys, the process is straightforward:
type ActionMap = {
[K in keyof Book]: ActionSpecific<K>
}
type ActionUnion = ActionMap[keyof Book]
ActionUnion
results in
ActionSpecific<"title"> | ActionSpecific<"chapters">
, ensuring the maintenance of pairings. Using
ActionSpecific<keyof Book>
would lead to the loss of pairing, similar to the previous scenario.
(Optional) Specify the return type of setBookProperty
as ActionSpecific<K>
:
const setBookProperty = <K extends keyof Book>(propertyName: K, propertyValue: Book[K]): ActionSpecific<K> => ({
payload: { propertyName, propertyValue },
});
Assign ActionUnion
as the type for your reducer function's action
, enabling the switch
statement to differentiate between union members:
const reducer = (action: ActionUnion) => {
switch (action.payload.propertyName) {
case 'chapters': {
const x = action.payload.propertyValue.map(s => s === 'Chapter 1');
}
}
};