Reputation: 8291
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
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