Search code examples
reactjstypescriptinference

Difficulties with Typescript args type inference


I am having trouble understanding an inference error. The ts linter underline (event: E) of useCallback with the message bellow. When I cast the callback of useCallback with as T the linter message is gone. Is there any way to avoid doing this? What did I didn't understand?

Complete code:

import { DependencyList, useCallback } from 'react'

const useStopBubblingCallback = <T extends (event: E) => any, E extends Event = Event>(
  callback: T,
  deps: DependencyList
) : T =>
  useCallback<T>((event: E) => {
    event.stopPropagation()
    event.preventDefault()
    return callback(event)
  }, deps)

export default useStopBubblingCallback

Linter message:

Argument of type '(event: E) => any' is not assignable to parameter of type 'T'.
  '(event: E) => any' is assignable to the constraint of type 'T', 
but 'T' could be instantiated with a different subtype of constraint 
'(event: E) => any'.ts(2345)

Solution

  • You can change useStopBubblingCallback to the following signature (example):

    const useStopBubblingCallback = <E extends Event = Event>(
      callback: (event: E) => any,
      deps: DependencyList
    ) : (event: E) => any =>
      useCallback((event: E) => { /*your code*/ }, deps)
    
    const cb = useStopBubblingCallback((event: MouseEvent) => {}, []) 
    // cb: (event: MouseEvent) => any
    

    Note, that in your case type parameter E had no inference candidate in the outer function, so TypeScript cannot do anything useful with E (it would be fixed to Event all the time).

    We also drop the manual type argument in useCallback<T> to let the compiler automatically infer its type.