[英]incompatible function argument in component props
我有一個組件,它包含一個已知具有 ID 的項目列表,以及一個用於過濾這些項目的 function。
帶有 ID 的類型是項目的通用類型,所有項目都將具有。
但更具體的項目將包括其他道具。
type GenericItem = {
id: string;
}
type SpecificItem = {
id: string;
someOtherProp: boolean;
}
我還有一個 function 類型,它使用通用類型進行操作。
type GenericItemFunction = (item: GenericItem) => boolean;
然后我有這個組件在其道具中使用 GenericItem 和 GenericItemFunction。
type CompProps = {
fn: GenericItemFunction;
items: GenericItem[];
}
const Comp: React.FC<CompProps> = ({ fn, items }) => <></>;
當我嘗試將此組件與特定類型一起使用時,出現錯誤提示我無法使用 GenericItemFunction 的實現,因為項目的類型不兼容。
const App = () => {
const items: SpecificItem[] = [];
const filter = (item: SpecificItem) => item.someOtherProp;
return (
<Comp
fn={filter} // error on `fn` prop
items={items}
/>
)
}
我收到的 typescript 錯誤是:
Type '(item: SpecificItem) => boolean' is not assignable to type 'GenericItemFunction'.
Types of parameters 'item' and 'item' are incompatible.
Property 'someOtherProp' is missing in type 'GenericItem' but required in type 'SpecificItem'.
我想我有兩個問題;
首先,當兩種類型都需要id: string
屬性時,為什么會發生沖突?
其次,有沒有更理智的方法來做這樣的事情?
我的第一個想法是GenericItemFunction
上的item
類型可以從提供給 App 組件中的items
prop 的值中推斷出來。
但老實說,我不確定那會是什么樣子……
我的另一個想法是讓Comp
成為通用的,但不顯示使用使用 generics 的反應組件......似乎 jsx/tsx 並不真正支持該語法。
我希望這樣的事情會引發各種錯誤。
const Comp = <T extends GenericItem,>({ fn, items }) => <></>;
const App = () => {
return <Comp<SpecificType> />;
}
最后,我確實嘗試了這個並且沒有任何錯誤。 但缺點是items
的類型被推斷為任何類型。
type GenericItem = {
id: string;
}
type SpecificItem = {
id: string;
someOtherProp: boolean;
}
type GenericItemFunction <T> = (item: T) => boolean;
type CompProps <T extends GenericItem = any> = {
fn: GenericItemFunction<T>;
items: T[];
}
const Comp: React.FC<CompProps> = ({ fn, items }) => <></>;
const App = () => {
const items: SpecificItem[] = [];
const filter = (item: SpecificItem) => item.someOtherProp;
return (
<Comp
fn={filter}
items={items}
/>
)
}
這是我一直在使用的游樂場的鏈接。
更新:
為什么你使用 any 作為 GenericItem 類型的默認值? 如果沒有這個,我相信它應該從 GenericItemFunction 正確地推斷出 Genericitem。 – tymzap
刪除CompProps
typedef 的= any
會導致Comp
聲明中出現錯誤...
type CompProps <T extends GenericItem> = {
fn: GenericItemFunction<T>;
items: T[];
}
const Comp: React.FC<CompProps> = ({ fn, items }) => <></>; // this line has the error
Generic type 'CompProps' requires 1 type argument(s).
意思是,我仍然必須在某處聲明類型。 這意味着在使用組件之前我需要知道GenericItem
類型的變體。
SpecificItem
只是碰巧與GenericItem
typedef 重疊的類型的表示。
在大多數情況下, Comp
不知道實際將使用什么類型,並且 any 不會向作者提供任何有用的信息。
我希望有類似...
type CompProps <T extends GenericItem> = {
items: <T extends GenericItem>[];
fn: <infer from T>[];
}
但我不確定這是否存在,或者類似的東西。
:face_palm:
使用<T extends GenericType = any>
的CompProps
是執行此操作的正確方法。
type CompProps <T extends GenericItem = any> = {
items: T[];
filter: (item: T) => boolean;
}
const Comp: React.FC<CompProps> = (props) => <></>;
魔法在這里發生:
const App = () => {
...
const filter = (item: SpecificItem) => item.someOtherProp;
...
}
const Comp: React.FC<CompProps>
不關心泛型類型是什么,因此= any
。 只要它至少具有與GenericType
相同的形狀。
但是在App
組件內部,我們在其中定義了 filter 屬性的方法,我們在參數中聲明了SpecificItem
typedef。 只有該方法使用SpecificItem
typedef。 CompProps
只關心它是否滿足GenericItem
所需的形狀。
希望對某人有所幫助。
在大多數情況下,
Comp
不知道實際將使用什么類型,並且 any 不會向作者提供任何有用的信息。
對我來說,這意味着Comp
是通用的。 所以我會刪除CompProps
上的= any
:
type CompProps<T extends GenericItem> = {
fn: GenericItemFunction<T>;
items: T[];
};
...然后將Comp
定義為通用的。 可能有一種方法可以用React.FC
做到這一點,但我個人不使用React.FC
所以我不知道它是什么。 :-) React.FC
不會為你做任何事情,正如一些人指出的那樣,除其他外,它說你的組件接受或不接受children
。 當我的組件接受children
時,我更願意明確說明這一點。
所以我會這樣定義Comp
:
const Comp = <T extends GenericItem>({ fn, items }: CompProps<T>) => {
// Just to show that `fn` and `items` play together:
const example = items.map(fn);
return <>{example}</>;
};
如果你想讓它有孩子,你可以很容易地添加它們。 如果你只想支持文本,請使用string
,如果你想允許任何內容,請使用React.Node
:
// Does allow children:
type Comp2Props<T extends GenericItem> = {
fn: GenericItemFunction<T>;
items: T[];
children: React.ReactNode;
};
const Comp2 = <T extends GenericItem>({ fn, items, children }: Comp2Props<T>) => {
// Just to show that `fn` and `items` play together:
const example = items.map(fn);
return <>{example}</>;
};
無論哪種方式,它都適用於您的場景:
const App = () => {
const items: SpecificItem[] = [];
const filter = (item: SpecificItem) => item.someOtherProp;
return <>
<Comp
fn={filter}
items={items}
/>
</>;
};
這些類型意味着它在您可能不希望它工作的地方不起作用:
const App2 = () => {
const genericItems: GenericItem[] = [];
const filterGeneric = (item: GenericItem) => item.id === "example"; // Silly definition, but...
const specificItems: SpecificItem[] = [];
const filterSpecific = (item: SpecificItem) => item.someOtherProp;
return <>
{/* Desirable error, `fn` can't handle `GenericItem`s, only `SpecificItem`s */}
<Comp
fn={filterSpecific}
items={genericItems}
/>
{/* Works, `fn` handles the supertype of the items, which is fine */}
<Comp
fn={filterGeneric}
items={specificItems}
/>
{/* Desirable error because `Comp` doesn't [now] support `children` */}
<Comp
fn={filterSpecific}
items={specificItems}
>
children here
</Comp>
{/* Works because `Comp2` does support `children`*/}
<Comp2
fn={filterSpecific}
items={specificItems}
>
children here
</Comp2>
</>;
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.