TypeScript:如何为Reducer的set_value动作推断动态键值类型?
set_value Action Payload To create a type-safe payload for your set_value action that adapts to any key in IState and enforces the correct value type, you can use TypeScript mapped types with either a union payload type or a generic action approach. Here's how to implement both solutions:
1. Union Payload Type (Direct Action Definition)
First, define a payload type that is a union of all valid single-key objects from IState:
interface IState { car_brand: string; year: number; // Add other properties here } enum ACTION_TYPE { set_value = 'set_value', } // Create a union type where each member is an object with one key from IState and its matching value type type SetValuePayload = { [K in keyof IState]: { [Key in K]: IState[Key] } }[keyof IState]; // Define the action type using the union payload type IAction = { type: ACTION_TYPE.set_value; payload: SetValuePayload; };
How This Works:
SetValuePayloaditerates over every key inIState, creates a type for each key-value pair, then converts this mapped type into a union of those single-key objects.- For example, if
IStatehascar_brand(string) andyear(number),SetValuePayloadbecomes{ car_brand: string } | { year: number }. - TypeScript will block invalid payloads (like passing a string for
year) automatically.
2. Generic Action Type with Helper Function (Better for Action Creation)
For more intuitive action creation with automatic type inference, use a generic action type paired with a helper function:
interface IState { car_brand: string; year: number; // Add other properties here } enum ACTION_TYPE { set_value = 'set_value', } // Generic action type tied to a specific key K from IState type IAction<K extends keyof IState> = { type: ACTION_TYPE.set_value; payload: { [Key in K]: IState[Key] }; }; // Helper function to create type-safe set_value actions function setValue<K extends keyof IState>(key: K, value: IState[K]): IAction<K> { return { type: ACTION_TYPE.set_value, payload: { [key]: value } as { [Key in K]: IState[Key] }, }; }
How This Works:
- The generic
IAction<K>links the payload's key and value type directly to a specific property inIState. - The
setValuehelper infers the key type from your input, so you get instant type checks:setValue('car_brand', 'Toyota')is valid (string value for string key).setValue('year', '2023')throws a TypeScript error (expected number, got string).
Reducer Implementation
Whichever approach you choose, the reducer can handle the action by extracting the key from the payload and updating the state:
function reducer(state: IState, action: IAction<keyof IState> | IAction): IState { switch (action.type) { case ACTION_TYPE.set_value: // Extract the single valid key from the payload const [key] = Object.keys(action.payload) as [keyof IState]; return { ...state, [key]: action.payload[key], }; default: return state; } }
Key Notes:
- Our payload types enforce exactly one key-value pair, so
Object.keys(action.payload)[0]will always return a valid key fromIState. - The type assertion
as [keyof IState]tells TypeScript the extracted key is a valid property ofIState.
This setup ensures full type safety for modifying any property in IState with your set_value action.
内容的提问来源于stack exchange,提问作者Colin Turner

