正确键入返回带有forwardRef的组件的HOC组件

问题描述

我正在尝试制作一个HOC来返回带有forwardRef的组件,但不确定如何键入它 这是代码

type Omitted = 'variant' | 'size';

export interface InputProps<T extends Omit<T,Omitted>> {
    startIcon?: React.ReactElement;
    endIcon?: React.ReactElement;
}

const withInput = <P extends object>(InputComponent: React.ComponentType<P>) =>
    React.forwardRef<HTMLInputElement,InputProps<P>>(
        ({ startIcon,endIcon,...props },ref) => {
            return (
                <InputGroup>
                    {startIcon && (
                        <InputLeftElement>
                            {startIcon}
                        </InputLeftElement>
                    )}
                    <InputComponent ref={ref} {...props} />
                    {endIcon && (
                        <InputRightElement>
                            {endIcon}
                        </InputRightElement>
                    )}
                </InputGroup>
            );
        }
    );

const Input = withInput(InputBaseComponent);

Input.Number = withInput(NumberInputBaseComponent);

但是出现两个错误 InputComponent

上的一个
Type '{ children?: ReactNode; ref: ((instance: HTMLInputElement | null) => void) | MutableRefObject<HTMLInputElement | null> | null; }' is not assignable to type 'P'.
  '{ children?: ReactNode; ref: ((instance: HTMLInputElement | null) => void) | MutableRefObject<HTMLInputElement | null> | null; }' is assignable to the constraint of type 'P',but 'P' Could be instantiated with a different subtype of constraint 'object'

,另一个Input.Number

Property 'Number' does not exist on type 'ForwardRefExoticComponent<InputProps<Pick<InputProps,"variant" | "size" | "left" | "right" | "form" | "p" | "slot" | "style" | "title" | "pattern" | "ref" | "key" | "sx" | "accept" | "alt" | "autoComplete" | ... 514 more ... | "isLoading"> & Pick<...>> & RefAttributes<...>>'.

如果有人想尝试一下,这里是指向codesandBox链接https://codesandbox.io/s/dazzling-shape-rwmmg?file=/src/Input.tsx:0-959

解决方法

属性“编号”不存在

更容易理解Input.Number错误。您正在尝试导出一个对象,该对象是一个组件,并且具有属性Number,而该属性是另一个组件。该错误告诉您不能在组件上设置Number之类的任意属性,这有点道理(这是可行的,但很复杂)。我建议将BaseNumber置于同一级别,而不是将其中一个作为另一个属性。

const Input = {
  Base: withInput(InputBaseComponent),Number: withInput(NumberInputBaseComponent)
}

现在Input只是一个具有两个不同组成部分作为属性的对象。

您还可以分别导出各个实例,并在导入时将它们分组在一起

import * as Input from `./Input`

将...道具分配给“ P”

首先,我认为您的InputProps界面没有达到您的预期目的。

export interface InputProps<T extends Omit<T,Omitted>> {
  startIcon?: React.ReactElement;
  endIcon?: React.ReactElement;
}

此接口表示InputProps是带有可选startIconendIcon的对象。而已。 T从未使用过,也没有关系。 T中不包含InputProps的道具。当您将InputProps扩展到{ startIcon,endIcon,...props }时,...props中剩下的唯一道具是children。因此,当我们希望...props包含P的所有道具InputComponent时,这当然会导致错误。

我认为您在这里要说的是InputProps<T>包含这两个图标以及T的全部,除了省略的属性。

export type InputProps<T> = Omit<T,Omitted> & {
  startIcon?: React.ReactElement;
  endIcon?: React.ReactElement;
}

这更好,因为现在...props中有一些实际的道具。将鼠标悬停在上面会显示

Pick<React.PropsWithChildren<InputProps<P>>,"children" | Exclude<Exclude<keyof P,Omitted>,"startIcon" | "endIcon">>

因此,基本上我们得到的所有内容都是P加上children减去variantsizestartIconendIcon。此子集可分配给P吗?也许但并非总是如此,这就是错误告诉您的内容。

使用{...rest}语法时,打字稿通常很难理解这些部分组成了整体。我经常不得不在我的HOC中声明as P

不管接下来几段的内容如何,​​您仍然要在这里结束:

<InputComponent {...props as P} ref={ref} />

类型安全

可以肯定地说...propsP吗?当我们使用as时,我们基本上是在告诉打字稿安静,因为我们知道的不止于此。因此,我们要确保不会给您错误的信息。

{... props} 何时可以分配给P?我们如何防止这些实例发生?

由于我们Omit variantsize来自内部组件的道具,所以...props(我们想要打字稿知道Ptype P)不包含这两个属性。如果这些属性存在于...props上并且是必需的,则P 不能P。如果startIcon包含必需的属性endIconvariant,情况也是如此,因为我们随价差将其拉出。

对于sizestartIcon,我根本看不到忽略它们的意义。您没有为它们设置任何默认值,那么为什么不让它们通过呢?

但是作为一个普遍的想法,对于endIconP extends,我们需要完善P,以便如果InputComponent具有这些属性,则它们 是可选的。我们还想确保我们的ref可以接受我们正在传递的as类型。

如果您对以下类型进行细化,您可以更加确信我们用interface Dropped { variant?: any; size?: any; } interface Icons { startIcon?: React.ReactElement; endIcon?: React.ReactElement; } export type InputProps<T> = Omit<T,keyof Dropped | "ref"> & Icons; type PBase = { ref?: React.Ref<HTMLInputElement>; } & Dropped & Partial<Record<keyof Icons,any>> const withInput = <P extends PBase>(InputComponent: React.ComponentType<P>) => .... 所断言的内容是正确的:

{{1}}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...