Javascript 正确键入返回forwardRef组件的HOC组件

Javascript 正确键入返回forwardRef组件的HOC组件,javascript,reactjs,typescript,higher-order-components,react-forwardref,Javascript,Reactjs,Typescript,Higher Order Components,React Forwardref,我正在尝试创建一个HOC,它返回一个带有forwardRef的组件,但不确定如何键入它 这是密码 type Omitted = 'variant' | 'size'; export interface InputProps<T extends Omit<T, Omitted>> { startIcon?: React.ReactElement; endIcon?: React.ReactElement; } const withInput = <

我正在尝试创建一个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);
另一个在
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<...>>'.
类型“ForwardRefExoticComponent”上不存在属性“Number”。
如果有人想试用,这里有一个指向codesandbox的链接:

属性“Number”不存在
Input.Number
错误更容易理解。您正试图导出一个对象,该对象是一个组件,并且其属性<代码>编号是另一个组件。错误告诉您不能在组件上设置任意属性,如
Number
,这有点道理(这是可行的,但很复杂)。我建议将
Base
Number
放在同一个级别,而不是将其中一个作为另一个的属性

const Input = {
  Base: withInput(InputBaseComponent),
  Number: withInput(NumberInputBaseComponent)
}
现在,
Input
只是一个具有两个不同组件作为属性的对象

export type InputProps<T> = Omit<T, Omitted> & {
  startIcon?: React.ReactElement;
  endIcon?: React.ReactElement;
}
您还可以单独导出各种实例,并在通过执行以下操作导入时将它们分组在一起

import * as Input from `./Input`
将…道具分配给“P” 首先,我认为你的
InputProps
接口没有达到你想要的效果

export interface InputProps<T extends Omit<T, Omitted>> {
  startIcon?: React.ReactElement;
  endIcon?: React.ReactElement;
}
这更好,因为现在我们在
…props
中有了一些实际的道具。在它上面盘旋显示

Pick<React.PropsWithChildren<InputProps<P>>, "children" | Exclude<Exclude<keyof P, Omitted>, "startIcon" | "endIcon">>
类型安全 可以说,
…道具是
p
?当我们将
用作
时,我们基本上是在告诉typescript闭嘴,因为我们知道的比它多。所以我们要确保我们没有给它提供坏信息

什么时候{…props}不能分配给
p
?我们如何防止这些情况发生

这里有一些值得关注的原因,因为我们从内部组件的道具中
省略了
变量
大小
,所以typescript知道
…道具
(我们希望是
p
)不包含这两个属性。如果这些属性存在于
类型P
上并且是必需的,则
…props
不能是
P
。如果
P
包含所需的属性
startIcon
endIcon
,则情况也是如此,因为我们将这些属性与排列一起取出

对于
variant
size
,我真的不认为省略它们有什么意义。您没有为它们设置任何默认值,为什么不让它们通过呢

但是作为一个总体想法,对于
startIcon
endIcon
,我们需要改进
p扩展
,以便如果
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>) => ....
接口已删除{
变体?:任何;
尺寸?:任何尺寸;
}
界面图标{
startIcon?:React.ReactElement;
endIcon?:React.ReactElement;
}
导出类型InputProps=省略图标(&I);
类型PBase={
ref?:React.ref;
}已删除和部分(&D)
常量withInput=(InputComponent:React.ComponentType

)=>。。。。

<InputComponent {...props as P} ref={ref} />
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>) => ....