[英]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
這樣的任意屬性,這有點道理(這是可行的,但很復雜)。 我建議將Base
和Number
放在同一級別,而不是將一個作為另一個的屬性。
const Input = {
Base: withInput(InputBaseComponent),
Number: withInput(NumberInputBaseComponent)
}
現在Input
只是一個具有兩個不同組件作為屬性的對象。
您還可以單獨導出各種實例,並在導入時將它們組合在一起
import * as Input from `./Input`
首先,我認為您的InputProps
接口沒有按照您的意圖執行。
export interface InputProps<T extends Omit<T, Omitted>> {
startIcon?: React.ReactElement;
endIcon?: React.ReactElement;
}
這個接口表示InputProps
是一個帶有可選startIcon
和endIcon
的對象。 就是這樣。 T
從來沒有被使用過,也沒有關系。 InputProps
不包含T
道具。 當您將InputProps
傳播到{ startIcon, endIcon, ...props }
中唯一剩下的...props
是children
。 因此,當我們希望...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
減去variant
、 size
、 startIcon
和endIcon
。 這個子集可以分配給P
嗎? 也許,但並非總是如此,這就是錯誤告訴你的。
當使用{...rest}
語法時,打字稿很難理解這些部分構成了整體,這是很常見的。 我經常不得不在我的 HOC 中聲明as P
無論接下來幾段中的所有內容如何,您最終仍將在此處執行此操作:
<InputComponent {...props as P} ref={ref} />
真的可以說...props
是P
嗎? 當我們使用as
,我們基本上是在告訴 typescript 安靜,因為我們知道的比它多。 因此,我們希望確保不會向其提供不良信息。
什么時候 {...props}不能分配給P
? 我們如何防止這些情況發生?
由於我們從內部組件的 props 中Omit
variant
和size
,所以這里有一些值得關注的原因,所以...props
(我們希望它是P
)被打字稿知道不包含這些屬性中的任何一個。 如果這些屬性存在於type P
並且是必需的,那么...props
不能是P
。 如果P
包含必需的屬性startIcon
和endIcon
,因為我們將它們與價差一起取出。
對於variant
和size
,我根本看不出省略它們的意義。 您沒有為它們設置任何默認值,那么為什么不讓它們通過呢?
但是作為一般的想法,對於startIcon
和endIcon
,我們需要改進我們的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.