Home > database >  Allow temporarily empty object for a typed state
Allow temporarily empty object for a typed state

Time:12-25

I'm coming from JS background and learning Typescript now. I can't get over this issue. I have a very particularly typed state that I know I will need in the future to work with:

type NotificationValuesType = {
  channel: string
  for: NotificationUsageTypes
  id: string
  type: NotificationTypes
  workspace: string
}[]

I'm setting my React state like this:

  const [dropdownsState, setDropdownsState] = useState<NotificationValuesType>([])

The thing is that initially I can now only type and id, all of the rest of props I can gather thru a series of dropdowns when a user chooses them, event fires, and then populates the state with one dropdown at a time, so at some point, it will be only: [{id: "id", type: "type", channel: "channel"}] at the next event, it will be [{id: "id", type: "type", channel: "channel", workspace: "workspace"}] and one more step and state update to get to know all the props in the declared type.

What I don't understand is how to tell Typescript to stop yelling at me until the point I know all the props and make sure I will know all the needed props in the future.

  • I absolutely cannot make any of these props optional because they
    aren't optional.
  • Also tried setting state type to NotificationValuesType | [] but typescript keeps yelling
  • I learned about type assertions and guess it should help but can't find any example of using it on the state. Can I do something like const [dropdownsState, setDropdownsState] = useState as <NotificationValuesType>([]) ???

Thanks for reading all way to the end! =)

CodePudding user response:

I absolutely cannot make any of these props optional because they aren't optional.

Perhaps not when you're done, but while you're in the process of building it, if values need to be undefined, then the type needs to reflect that. So most likely you'll want to have the state variable have optional properties, then you go through your steps to fill it out, and once you've verified it's all there you can assign it to a variable with a type where the properties are all mandatory.

There is a helper type called Partial which will take in a type and produce a new one who's properties are optional.

// Note: this type is just the individual object, not the array that you have
type Example = {
  channel: string
  for: NotificationUsageTypes
  id: string
  type: NotificationTypes
  workspace: string
}

const [dropdownsState, setDropdownsState] = useState<Partial<Example>[]>([])

// Later on, once you've verified that all the properties exist you can 
// assert that it's done. I don't know exactly what your verification 
// code will look like, but here's an example
if (dropdownsState.every(value => {
  return value.channel && value.for && value.id && value.type && value.workspace
})) {
  const finishedState = dropdownsState as Example[];
  // do something with the finished state
}

EDIT: as pointed out in the comments, if you use a type guard, then typescript can narrow down the types and save you from having to reassign it:

if (dropdownsState.every((value): value is Example => {
  return value.channel && value.for && value.id && value.type && value.workspace
})) {
  // Inside this block, typescript knows that dropdownsState is an Example[]

}
  • Related