【问题标题】:Converting a generic React Component to TypeScript throws error将通用 React 组件转换为 TypeScript 会引发错误
【发布时间】:2020-07-22 14:28:11
【问题描述】:

我正在尝试将 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 &lt;Component .../&gt; 附近显示'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(' ') 也一样。

我正在为此失去理智。似乎无法弄清楚。会喜欢任何指针吗?

【问题讨论】:

  • VSCode 显示的错误是什么?此外,如果您只想忽略它们而不实际修复它们,请在行上方添加 // @ts-ignore(当然,您应该尝试修复它们)。
  • 您是否尝试过将变量 Component 重命名为其他名称?
  • @EmreKoc 组件上的第一个错误写在帖子中。阅读const Component = React.Component | string 正下方的行。我很乐意解决它而不是忽略它。
  • @FaisalRashid 没有必要重命名它,因为Component 不是保留关键字,或者我正在从react 本身解构Component 所以我认为即使我也应该没问题重命名它:)

标签: javascript reactjs typescript react-ref


【解决方案1】:

我无法直接使用它来解决它,因为我认为 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)

【讨论】:

    【解决方案2】:

    对于 Component 部分,在 props 中设置它而不是一个新变量:

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

    【讨论】:

    • 我已经在Props 中做到了。请参阅type Props 定义。我不明白我的代码和你的代码有什么区别?
    • 你的是设置道具的类型,我的是把道具从包装器重命名为组件,所以你可以将它用作标签。基本上你试图用 const 做的,没有 const。
    • 哦,明白了。但它仍然让我在 return &lt;Component /&gt;'Component' refers to a value, but is being used as a type here. Did you mean 'typeof Component'?ts(2749) 时出错
    • 这个答案可能会有所帮助:stackoverflow.com/questions/55969769/…
    【解决方案3】:

    我认为您必须将wrapper 设置为React.ComponentType&lt;React.PropsWithRef&lt;any&gt;&gt;,这是准确的 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"]
    },
    

    【讨论】:

    • 所以flat() 的事情奏效了,但遗憾的是我仍然遇到其他 3 个错误。我完全按照你说的做了。
    • 其他 3 个是什么?
    • 第一个是Component 上的那个,然后typicalRef.currentArgument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.ts(2345) 最后在className={classNames.join(' ')} 它说Type 'boolean' is not assignable to type 'string'.ts(2322)classNameclassNames 它说The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.ts(2362)
    • 根据我的建议你改了之后会出现吗?如果是这样,我可以再次查看您完全编辑的代码吗?
    • 我刚刚进行了编辑以显示我的建议。如果您的错误消失了,您可以复制并再次检查?
    猜你喜欢
    • 2018-02-22
    • 2014-07-03
    • 1970-01-01
    • 2018-03-02
    • 1970-01-01
    • 2020-11-07
    • 2018-07-18
    • 1970-01-01
    • 2019-12-13
    相关资源
    最近更新 更多