reactjstypescriptnext.jsheadless-ui

How to specify types in TypeScript?


I am trying to use the code example below in Next.js 15 (TypeScript), but getting the error on the line onChange={setSelectedPerson} and would like to know how to specify the types on passing:

Type error: Type 'Dispatch<SetStateAction<{ id: number; name: string; }>>' is not assignable to type '(value: NoInfer<{ id: number; name: string; }> | null) => void'.
  Types of parameters 'value' and 'value' are incompatible.
    Type 'NoInfer<{ id: number; name: string; }> | null' is not assignable to type 'SetStateAction<{ id: number; name: string; }>'.
      Type 'null' is not assignable to type 'SetStateAction<{ id: number; name: string; }>'.
import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions anchor="bottom" className="border empty:invisible">
        {filteredPeople.map((person) => (
          <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100">
            {person.name}
          </ComboboxOption>
        ))}
      </ComboboxOptions>
    </Combobox>
  )
}

Solution

  • Here’s how you can fix this issue:

    import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
    import { useState } from 'react'
    
    // Define the type for people
    interface Person {
      id: number
      name: string
    }
    
    const people: Person[] = [
      { id: 1, name: 'Durward Reynolds' },
      { id: 2, name: 'Kenton Towne' },
      { id: 3, name: 'Therese Wunsch' },
      { id: 4, name: 'Benedict Kessler' },
      { id: 5, name: 'Katelyn Rohan' },
    ]
    
    function Example() {
      // Allow selectedPerson to be null as well
      const [selectedPerson, setSelectedPerson] = useState<Person | null>(people[0])
      const [query, setQuery] = useState('')
    
      const filteredPeople =
        query === ''
          ? people
          : people.filter((person) => {
              return person.name.toLowerCase().includes(query.toLowerCase())
            })
    
      return (
        <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
          <ComboboxInput
            aria-label="Assignee"
            displayValue={(person: Person | null) => person?.name || ''}
            onChange={(event) => setQuery(event.target.value)}
          />
          <ComboboxOptions anchor="bottom" className="border empty:invisible">
            {filteredPeople.map((person) => (
              <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100">
                {person.name}
              </ComboboxOption>
            ))}
          </ComboboxOptions>
        </Combobox>
      )
    }
    
    export default Example