xobotyi/react-scrollbars-custom

TypeScript React onScroll event handler error

gerainlotZS opened this issue · 3 comments

Using react-scrollbars-custom with TypeScript what would be the correct implementation of the onScroll event?
Edit: I suspect there's some kind of namespace conflict happening between TypeScript's general definition for onScroll and the plugin; finding references for onScroll has two results, which reflects the error given at the bottom of this question, specifically not assignable to type '((event: UIEvent) => void) & ((scrollValues: ScrollValues, prevScrollValues: ScrollValues) => void)

import * as React from "react";
import { Scrollbar } from 'react-scrollbars-custom';

export default class App extends React.Component  {

    handleScroll = (scrollValues : ScrollValues, prevScrollValues : ScrollValues)  => {
        // ...      
    }

    render() {

        return <Scrollbar onScroll={this.handleScroll} style={{ position: "absolute", height:"100%", width:"100%" }}>
        // ...
        </Scrollbar>
    }   
}

This is the error message:

Error  TS2322  (TS) Type '(scrollValues: ScrollValues, prevScrollValues: ScrollValues) => void' is not assignable to type '((event: UIEvent<HTMLElement>) => void) & ((scrollValues: ScrollValues, prevScrollValues: ScrollValues) => void)'.
  Type '(scrollValues: ScrollValues, prevScrollValues: ScrollValues) => void' is not assignable to type '(event: UIEvent<HTMLElement>) => void'

@gerainlotZS were you able to find a workaround for this?

Ok so I was able to figure out a TypeScript workaround for this:

First, you'll need to create a type predicate util to be able to distinguish between the UIEvent and the actual ScrollState

function isScrollState(
  eventOrState: React.UIEvent<HTMLDivElement, UIEvent> | ScrollbarState
): eventOrState is ScrollbarState {
  return 'clientHeight' in eventOrState;
}

Second, define the actual onScroll handler

function onScroll(
  state: React.UIEvent<HTMLDivElement, UIEvent> | ScrollState,
  prevState?: ScrollState
): void {
  if (isScrollState(state)) {
    // Can now use all of the properties of the ScrollState like scrollTop or clientWidth 👍
  }
}

-- OR --

If you're using both the current and previous state you can use a function overload to match the props defined type better

function onScroll(event: React.UIEvent<HTMLDivElement, UIEvent>): void
function onScroll(state: ScrollState, prevState: ScrollState): void
function onScroll(
  event: React.UIEvent<HTMLDivElement, UIEvent> | ScrollState,
  prevState?: ScrollState
): void {
  if (isScrollState(event) && prevState) {
    // safely use event as ScrollState and prevState is defined as well
  }
}

@brandonburrus Just a quick notification if someone runs into this one. It is ScrollState only, no ScrollbarState. They are completely different but they both exist.