将通用 React 组件转换为 TypeScript 会抛出错误

Converting a generic React Component to TypeScript throws error

我正在尝试将 https://github.com/catalinmiron/react-typical 移植到 TypeScript。但是,我遇到了一些问题。

这是 VSCode 中有错误的屏幕截图:

为简洁起见,下面是相同的代码:

import React from 'react'
import { type, type as loopedType } from '@camwiegert/typical'
import styles from './styles.module.css'

type Props = {
    steps: Array<any>
    loop: number
    className: string
    wrapper: React.Component
}

const Typical: React.FC<Props> = ({ steps, loop, className, wrapper = 'p' }) => {
    const typicalRef = React.useRef<HTMLElement>(null)
    const Component: string = wrapper
    const classNames: string[] = [styles.typicalWrapper]

    if (className) {
        classNames.unshift(className)
    }

    React.useEffect(() => {
        if (loop === Infinity) {
            type(typicalRef.current, ...steps, loopedType)
        } else if (typeof loop === 'number') {
            type(typicalRef.current, ...Array(loop).fill(steps).flat())
        } else {
            type(typicalRef.current, ...steps)
        }
    }, [typicalRef])

    return <Component ref={typicalRef} className={classNames.join(' ')} />
}

export default React.memo(Typical)

我无法为 Component 写字。

我也尝试过以下操作:

const Component = React.Component | string

但是它在 return <Component .../> 附近显示 'Component' refers to a value, but is being used as a type here. Did you mean 'typeof Component'?,在 Component 上方有下划线。

我也无法转换 typicalRef,因为 typicalRef.current 总是通过在其下方显示红色波浪线来抛出错误。 flat()classNames.join(' ').

也一样

我为此失去了理智。似乎无法弄清楚。想要任何指示吗?

对于组件部分,在道具中设置它而不是新变量:

const Typical: React.FC<Props> = ({ wrapper:Component = 'p' }) => {
    return <Component />
}

我认为你必须将你的 wrapper 设置为 React.ComponentType<React.PropsWithRef<any>> 这是准确的 React 类型并直接重命名你的包装器(不要在正文中重新分配 tsc 可以是与混合类型混淆为字符串),因此您的代码可能会更改如下:

type Props = {
 steps: Array<any>
 loop: number
 className: string
 wrapper: React.ComponentType<React.PropsWithRef<any>>
}

const Typical: React.FC<Props> = ({ steps, loop, className, wrapper: Component = 'p' }) => {
  const typicalRef = React.useRef<HTMLElement>()
  const classNames: string[] = [styles.typicalWrapper]

  if (className) {
    classNames.unshift(className)
  }

  React.useEffect(() => {
    if (loop === Infinity) {
        type(typicalRef.current, ...steps, loopedType)
    } else if (typeof loop === 'number') {
        type(typicalRef.current, ...Array(loop).fill(steps).flat())
    } else {
        type(typicalRef.current, ...steps)
    }
  }, [typicalRef]) 

  return <Component ref={typicalRef} className={classNames.join(' ')} />
}

因为 flat 方法仅适用于“es2019”,这意味着您必须通过添加到 tsconfig.json 以下内容来将其包含在 tsc 构建中:

"compilerOptions": {
  "lib": ["ES2019"]
},

我无法直接使用它来解决它,因为我认为 TypeScript 本身不支持它 https://github.com/microsoft/TypeScript/issues/28892

但我确实使用 React.createElement 语法解决了它。我的整个代码现在看起来像这样:

import React from 'react'
import { type, type as loopedType } from '@camwiegert/typical'
import styles from './styles.module.css'

type Props = {
    steps: Array<any>
    loop: number
    className?: string
    wrapper: keyof JSX.IntrinsicElements
} & React.HTMLAttributes<HTMLOrSVGElement>

const Typical = ({ steps, loop, className, wrapper: Wrapper = 'p' }: Props) => {
    const typicalRef: React.RefObject<HTMLElement> = React.useRef<HTMLElement>(null)
    const classNames: string[] = [styles.typicalWrapper]

    if (className) {
        classNames.unshift(className)
    }

    const typicalStyles: string = classNames.join(' ')

    React.useEffect(() => {
        if (loop === Infinity) {
            type(typicalRef.current as HTMLElement, ...steps, loopedType)
        } else if (typeof loop === 'number') {
            type(typicalRef.current as HTMLElement, ...Array(loop).fill(steps).flat())
        } else {
            type(typicalRef.current as HTMLElement, ...steps)
        }
    }, [typicalRef])

    return React.createElement(Wrapper, {
        ref: typicalRef,
        className: typicalStyles,
    })
}

export default React.memo(Typical)