renakre
renakre

Reputation: 8291

Typescript Error in React useScroll hook: Cannot invoke an expression whose type lacks a call signature

I have the following function definition:

const useScroll = () => {
    const ref = useRef(null)
    function executeScroll() {
        if (ref !== null)
            window.scrollTo(0, ref.current.offsetTop)
    }

    const htmlElementAttributes = { ref }

    return [executeScroll, htmlElementAttributes]
}

export default useScroll;

Based on this function, I have the following code:

const [executeScroll, scrollHtmlAttributes] = useScroll();

const click_argueBtn = (e) => {
    e.preventDefault();
    executeScroll();//error
}

However, the executeScroll(); code throws the following error:

Error: Cannot invoke an expression whose type lacks a call signature

Any ideas why I receive this error? My code is based on this post.

Upvotes: 1

Views: 551

Answers (1)

Nicholas Tower
Nicholas Tower

Reputation: 85012

Typescript is doing its best to determine the types automatically, and it determines that useScroll returns an array, who's elements are each () => void | { ref: /*ref type*/ } (i don't know precisely what the type on the ref object is). When you try to call executeScroll, it doesn't know whether it's a function, or an object with a ref. So since it might not be a function, you're not allowed to call it.

Instead, i'd recommend explicitly telling typescript that useScroll returns a tuple. A tuple is similar to an array, except you explicitly declare how many elements it has and what their individual types are:

const useScroll = (): [() => void, { ref: /* ref type */ }] => {
  // ...etc
}

or

const useScroll = () => {
  // ... etc
  return [executeScroll, htmlElementAttributes] as [() => void, { ref: /* ref type */ }];
}

or if you don't like it inline, you could extract it to a type:

export type UseScrollResult = [() => void, { ref: /* ref type */ }];
const useScroll = (): UseScrollResult => {
  // ...etc
}

From this, typescript now knows that element 0 of the array is () => void, and therefore it's legal to call it as a function.

Upvotes: 2

Related Questions