Home > Enterprise >  Can only call function exported/deconstructed with curly braces but not square brackets
Can only call function exported/deconstructed with curly braces but not square brackets

Time:05-09

In a basic React custom hook in which the only difference is returning with [ ] or { }:

const useCustomHookSquare= () => {
  const [state, setState] = useState<Type>(initialState);

  return [state, storeBounceBox];
}

const useCustomHookCurly= () => {
  const [state, setState] = useState<Type>(initialState);

  return {state, storeBounceBox};
}

This works:

const {state, setState} = useCustomHookCurly();
// or even
const {setState} = useCustomHookCurly();

But doing the exact same thing deconstructing with square brackets gives me a TypeScript error when calling the setState function (or any other function exported this way):

const [state, setState] = useCustomHookSquare();
setState(someState); // "This expression is not callable. Type has no call signatures".

I feel like there is some basic JavaScript concept I'm missing here. Why does the {} version allow me to call the function but the [] version doesn't?

CodePudding user response:

Because your useCustomHookSquare hook has no return type information TypeScript infers that it returns an array of two different types. But not both of the types that can be part of that array are functions, so you get a type error because one of the possible values isn't callable.

const useHook = () => {
  return ["item", () => true]
}

// The inferred value of useHook is now
//
// (string | (() => boolean))[]
//
// So when calling `fn`, TypeScript doesn't know if it will be a string or a function
const [item, fn] = useHook()

//This expression is not callable.
//  Not all constituents of type 'string | (() => boolean)' are callable.
//    Type 'string' has no call signatures.
fn()

Add some return types to your useCustomHookSquare function to fix the problem. Here's what my fixed example would look like.

const useHook = (): [string, () => true] => {
  return ["item", () => true]
}

TypeScript now understands that the returned array only has 2 items in it, and those items types should always be the same.

CodePudding user response:

By default typescript assigns a wider type to an array. So if you have:

const arr = ["str", 5];

The type will be an array of (string | number)[], not a tuple of [string, number].

To make it a tuple, use as const:

const useCustomHookSquare= () => {
  const [state, setState] = React.useState<string>();

  return [state, setState] as const;
}
  • Related