Set typescript for useState output to be one of the elements of an array of objects

Viewed 126

Given that the state that rangeOptionChosen is one of the range options, how do I set that in typescript for useState.


  const rangeOptions = [
    { label: "2 weeks", value: "2 weeks" },
    { label: "1 month", value: "1 month" },
  ];
  const [rangeOptionChosen, setRangeOptionChosen] = useState<WhatGoesHere?>(rangeOptions[0]);

"WhatGoesHere?" should be replaced with something that says that it is "one of the elements in the array of rangeOptions"

UPDATE: I am thinking more along the lines that WhatGoesHere is strictly one of the objects of rangeOptions. I guess it would be the equivilant of something like (though I don't think this is valid typescript):

useState<
    { label: "2 weeks", value: "2 weeks" }|
    { label: "1 month", value: "1 month" }>(rangeOptions[0])

But of course be able to accommodate a much larger list based on the array, and if someone tried to do

setRangeOptionChosen({ label: "cat", value: "dog" })

it would create a typescript error because it doesn't match one of the original range options.

1 Answers

There are multiple ways you can write the type for WhatGoesHere.

Interfaces

You can define interface outside the component like below

interface RangeOption {
  label: string;
  value: string;
}

and use it inside the component like below

const [rangeOptionChosen, setRangeOptionChosen] = useState<RangeOption>(rangeOptions[0]);

you can read more about interfaces here https://www.typescriptlang.org/docs/handbook/interfaces.html

Type

You can define type outside the component like below

type RangeOption = {
  label: string;
  value: string;
}

and use it inside the component like below

const [rangeOptionChosen, setRangeOptionChosen] = useState<RangeOption>(rangeOptions[0]);

You can read more about types https://www.typescriptlang.org/docs/handbook/2/basic-types.html

Also, you can use the built-in types provided by TypeScript like below

const [rangeOptionChosen, setRangeOptionChosen] = useState<Record<'label' | 'value', string>>(rangeOptions[0]);

Can read more about Record here https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type

Enum and interface combination

If you have very specific labels and values and want to restrict it to those labels and values then you can also use an enum to define that like below.

enum Label = {
  ONE_MONTH = '1 month',
  TWO_WEEKS = '2 weeks',
}

enum Value = {
  ONE_MONTH = '1 month',
  TWO_WEEKS = '2 weeks',
}

interface RangeOption {
  label: Label;
  value: Value;
}
Related