0

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

4

1 回答 1

0

属性“编号”不存在

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

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是一个带有可选startIcon和的对象endIcon。而已。 T从未使用过,也没有关系。的道具T不包含在InputProps. 当你传播InputProps{ startIcon, endIcon, ...props }时,剩下的唯一道具...propschildren。所以当我们想要...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、、、和。这个子集可以分配给吗?也许,但并非总是如此,这就是错误告诉你的。variantsizestartIconendIconP

{...rest}打字稿在使用语法时很难理解片段构成了整体是很常见的。我经常不得不as P在我的 HOC 中断言。

不管接下来几段中的所有内容,您最终仍将在这里执行此操作:

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

类型安全

真的可以这么说...propsP?当我们使用 时as,我们基本上是在告诉 typescript 保持安静,因为我们知道的比它多。因此,我们要确保我们不会向其提供不良信息。

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

这里有一些值得关注的原因,因为我们Omit variantsize来自内部组件的道具,所以...props(我们想要的P)被打字稿知道不包含这些属性中的任何一个。如果这些属性存在type P并且是必需的,则...props 不能P。如果P包含必需的属性startIcon和也是如此endIcon,因为我们将它们与价差一起拉出。

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

但作为一个总体思路,对于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>) => ....
于 2020-11-05T21:00:58.173 回答