Home > Back-end >  TypeScript changing type definition of a function after importing it to another file
TypeScript changing type definition of a function after importing it to another file

Time:05-29

I have a custom hook called useDropdownSelection. It's fairly simple. Here's the code:

import { useState } from 'react'

export const useDropdownSelection = (initialValue: string) => {
  const [selectedOption, setSelectedOption] = useState<string>(initialValue)

  const handleClick = (event: React.MouseEvent<HTMLLIElement>) => {
    const target = event.target as HTMLElement
    setSelectedOption(target.innerText)
  }

  return [selectedOption, handleClick]
}

As you can see, the handleClick function is of type (event: React.MouseEvent<HTMLLIElement>) => void

But when I import it to my index.tsx, the handleClick function, for whatever reason, becomes of type string | ((event: MouseEvent<HTMLLIElement, MouseEvent>) => void)

const [selectedDropdownOption, handleDropdownClick] = useDropdownSelection('Most upvotes') // const handleDropdownClick: string | ((event: MouseEvent<HTMLLIElement, MouseEvent>) => void). WHY?

Why is this happening?

I assume it has something to do with the fact that I'm returning an array?

CodePudding user response:

If you are returning an array in your Custom Hook, you will want to avoid type inference as TypeScript will infer a union type (when you actually want different types in each position of the array).

What you need to do if you want to avoid the union type is using a const assertion:

return [selectedOption, handleClick] as const;

Now selectedOption and handleClick will keep respectively their type.

Docs: https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/hooks/#custom-hooks

CodePudding user response:

It's because you don't specify that useDropdownSelection is going to return array of specific types for each indexes, so TS assumes that each index may contain both callback and string value. check example below:

const f = (s: string) => {
  const n: number = 5;
  return [s, n];
}
const result = f('s');

result is a (string | number)[] type. To explicitly inform that it's going to be [string, number] you have to implement it like below:

const f = (s: string): [string, number] => {
  const n: number = 5;
  return [s, n];
}
const result = f('s'); // type is [string, number]

Syntax presented above is similar to one that is implemented by useState.

So in your example explicitly type return value of a hook:

export const useDropdownSelection = (initialValue: string): [string, (event: MouseEvent<HTMLLIElement, MouseEvent>) => void] => {...}
  • Related