打字稿滚动窗口功能类型

时间:2021-04-20 19:49:20

标签: reactjs typescript

我正在尝试制作一个 React 组件,它允许用户将窗口滚动到屏幕顶部。我已经设置了组件和类型,但是我在设置窗口的属性时遇到了问题。我目前的错误是这样的:

<块引用>

元素隐式具有“any”类型,因为索引表达式不是“number”类型。

如何正确处理窗口?

import { throttle } from 'lodash'
import React, { useState } from 'react'
import { useEventListener } from '../../hooks'
import { Arrow } from './styles'

interface ScrollRest {
  showBelow: number,
  className: string,
  size: string
}

interface ScrollProps {
  direction: string,
  by: number,
  to: number,
  rest: ScrollRest
}

interface ScrollFunctionProps {
  mode: string, 
  to: number
}

export default function Scroll( { direction = `up`, by, to, rest }: ScrollProps ) {
  const { showBelow, className, size = `calc(0.6em + 30px)` } = rest
  if ( ![`up`, `down`].includes( direction ) )
    throw TypeError(
      `Scroll component's direction prop must be either 'up' or 'down'`
    )
  if ( to && ( typeof to !== `number` || to <= 0 ) )
    throw TypeError( `Scroll component's to prop must be a positive number` )
  if ( by && typeof by !== `number` )
    throw TypeError( `Scroll component's by prop must be a number` )

  const [show, setShow] = useState( showBelow ? false : true )

  const scroll = ( { mode, to }: ScrollFunctionProps ) =>
    window[`scroll` + mode]( { top: to, behavior: `smooth` } ) // <- Error here!

  const handleScroll = throttle( () => {
    if ( !showBelow ) return
    if ( window.scrollY > showBelow ) {
      if ( !show ) setShow( true )
    } else {
      if ( show ) setShow( false )
    }
  }, 300 )
  useEventListener( `scroll`, handleScroll )

  const handleClick = () => {
    if ( to ) scroll( { mode: `To`, to: to * window.innerHeight } )
    else if ( by ) scroll( { mode: `By`, to: by * window.innerHeight } )
    else if ( direction === `up` ) scroll( { mode: `To`, to: 0 } )
    else scroll( { mode: `To`, to: document.body.scrollHeight } )
  }

  const arrowProps = { show, direction, className, size }
  return <Arrow onClick={handleClick} {...arrowProps} />
}

1 个答案:

答案 0 :(得分:1)

最简单的更改是将 mode 类型限制为 'scrollTo' | 'scrollBy' 方法名称:

interface ScrollFunctionProps {
  mode: 'scrollTo' | 'scrollBy', 
  to: number
}

这允许 TypeScript 正确推断 window.scrollXXX(...) 方法调用的类型签名。

import { throttle } from 'lodash'
import React, { useState } from 'react'
import { useEventListener } from '../../hooks'
import { Arrow } from './styles'

interface ScrollRest {
  showBelow: number,
  className: string,
  size: string
}

interface ScrollProps {
  direction: string,
  by: number,
  to: number,
  rest: ScrollRest
}

interface ScrollFunctionProps {
  mode: 'scrollTo' | 'scrollBy', // restrict `mode` to known Window scroll method names
  to: number
}

export default function Scroll( { direction = `up`, by, to, rest }: ScrollProps ) {
  const { showBelow, className, size = `calc(0.6em + 30px)` } = rest
  if ( ![`up`, `down`].includes( direction ) )
    throw TypeError(
      `Scroll component's direction prop must be either 'up' or 'down'`
    )
  if ( to && ( typeof to !== `number` || to <= 0 ) )
    throw TypeError( `Scroll component's to prop must be a positive number` )
  if ( by && typeof by !== `number` )
    throw TypeError( `Scroll component's by prop must be a number` )

  const [show, setShow] = useState( showBelow ? false : true )

  const scroll = ( { mode, to }: ScrollFunctionProps ) =>
    window[mode]( { top: to, behavior: `smooth` } )

  const handleScroll = throttle( () => {
    if ( !showBelow ) return
    if ( window.scrollY > showBelow ) {
      if ( !show ) setShow( true )
    } else {
      if ( show ) setShow( false )
    }
  }, 300 )
  useEventListener( `scroll`, handleScroll )

  const handleClick = () => {
    if ( to ) scroll( { mode: `scrollTo`, to: to * window.innerHeight } )
    else if ( by ) scroll( { mode: `scrollBy`, to: by * window.innerHeight } )
    else if ( direction === `up` ) scroll( { mode: `scrollTo`, to: 0 } )
    else scroll( { mode: `scrollTo`, to: document.body.scrollHeight } )
  }

  const arrowProps = { show, direction, className, size }
  return <Arrow onClick={handleClick} {...arrowProps} />
}