繁体   English   中英

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

[英]Correctly typing a HOC component that returns a component with forwardRef

我正在尝试制作一个 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<...>>'.

如果有人想尝试,这里有一个代码沙盒的链接: 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`

将 ...props 分配给 'P'

首先,我认为您的InputProps接口没有按照您的意图执行。

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

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

我想你在这里想说的是InputProps<T>包含这两个图标和所有T ,除了省略的属性。

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

这更好,因为现在我们在...props有一些实际的...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 ,我们基本上是在告诉 typescript 安静,因为我们知道的比它多。 因此,我们希望确保不会向其提供不良信息。

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

由于我们从内部组件的 props 中Omit variantsize ,所以这里有一些值得关注的原因,所以...props (我们希望它是P )被打字稿知道不包含这些属性中的任何一个。 如果这些属性存在于type P并且是必需的,那么...props不能P 如果P包含必需的属性startIconendIcon ,因为我们将它们与价差一起取出。

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

但是作为一般的想法,对于startIconendIcon ,我们需要改进我们的P 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>) => ....

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM