簡體   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