Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking whether a string is a key in a type, in Typescript

Tags:

typescript

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.

like image 385
Jonathan Tuzman Avatar asked Jun 26 '26 23:06

Jonathan Tuzman


2 Answers

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)
like image 137
Jonathan Tuzman Avatar answered Jun 28 '26 17:06

Jonathan Tuzman


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
}
like image 43
Todd Skelton Avatar answered Jun 28 '26 17:06

Todd Skelton



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!