I have a type for my state, as well as an object of message handlers (thunks), which is explicitly typed as Record<string, (val: any) => AppThunk>
I get incoming messages that are objects, and their keys could match up with keys of the message handler object, in which case I want to call the message handler, OR their keys could match up with keys of the DataState, in which case I want to simply send them through to updateState.
The code below works fine.
type DataState = {
startRequestCounter: -1,
startResponseCounter: -1,
stopRequestCounter: -1,
stopResponseCounter: -1,
robotMode: RobotMode.Idle as RobotMode,
}
const messageHandlers: Record<string, (val: any) => AppThunk> = {
startRequestCounter: () => async (dispatch, getState) => // do stuff,
startResponseCounter: (counter: number) => dispatch => // do stuff,
stopResponseCounter: (counter: number) => dispatch => // do stuff
}
export const handleMessage = (message: MessagePayload): AppThunk => (dispatch, getState) => {
Object.entries(message).forEach(([snakeKey, value]) => {
const key = camelCase(snakeKey)
// call the message handler for this message, if there is one
if (messageHandlers[key]) {
dispatch(messageHandlers[key](value))
}
// if not, update the state if we have a state for this message
else if (Object.keys(getState().data).includes(key)) { // <~~~~ HERE!!!!
dispatch(updateState({ [key]: value }))
}
})
}
However, I feel like the else if at the end could be written more concisely.
else if (getState().data[key]) works (if I ts-ignore it), but TypeScript complains:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ startRequestCounter: number; startResponseCounter: number; stopRequestCounter: number; stopResponseCounter: number; cuvettesCount: number; pipettesCount: number; robotMode: RobotMode; }'.
I've done a little searching, and it looks like keyof might be what I'm looking for, but keyof can't even be found when I type it.
Todd's answer is good, but I figured one out that is basically as concise as I was looking for:
else if (key in getState().data)
It's because key is not guaranteed to be one of the keys listed in your DataState.
You can tell TypeScript that it will be using keyof.
if (getState().data[key]) {
// Error Here
}
if (getState().data[key as keyof DataState]) {
// Not Here
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With