簡體   English   中英

組件道具中的不兼容 function 參數

[英]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}
        />
    )
}

這是我一直在使用的游樂場的鏈接。

https://www.typescriptlang.org/play?#code/C4TwDgpgBA4hB2EBOBLAxgSWBAtlAvFAN4BQAkCgCYBcUAzsKvAOYDcJAviSaJFAMqQ0KAGbosuAsRJRZUKrQZM2MuXQD2OCAHlgAC2QAFJOrC0ARuvUAbCAEN47Lj3DQ4iVJmw4AYgFd4NGAUdXgoAB4AFQA+KQAKFG9aSIBKAljLG3tHbhc+AGFNMGNTOgjIqAgAD2x4SjL3ZHFvKQcQWMJSMhF4WkbPCV8AoJD4KOj2OXlvOmSAbQBdJxI0UIYoQpwzKAAleyCAOh988M3ikzA6Dqg4oigegBpp3DKONPxY8OjwgHoJ7lW8HWAEEwGB4u9YqQpoD1okXrRBBBhGIvLhFlJFpM5LDgPcUNZsEh4vCcIihKJmrhIc8cAcNFpdAYkCUwOxVLIkBBgH4kGE4hyphEzoKhXIevgiGJCcguGKxaS6JLFXL5X9BSlOEA

更新:

為什么你使用 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM