如何正确扩展TypeScriot接口?应该只有一个孩子



我正在使用一个私人公司的Accordion库。我使用的是TS 4.6.4和React 17。根据文档,如果有多个子节点,它会工作:

question
answer
question
answer
question
answer

但是我得到错误:

This JSX tag's 'children' prop expects a single child of type 'ReactChild | undefined', but multiple children were provided.

这个错误显示在:

<StyledAccordion onChange={onChange}>
<dt>Question(SBTi)?</dt>
<div>answer </div>
<dt>Question(SBTi)?</dt>
<div>answer </div>

StyledAccordion是一个样式化组件:

export const StyledAccordion = styled(Accordion)``

该组件的接口包括:

export interface AccordionProps {
....
....
children?: ReactChild;
....
....

因此,我试图扩展这个接口,使它通过TypeScript支持多个子接口…我认为这里的打字文稿不够完善……

我正在尝试这样做:

interface MultipleChildrenAccordion extends AccordionProps {
children?: React.ReactChild[]|React.ReactNode|undefined;
}

然而我得到:

Interface 'MultipleChildrenAccordion' incorrectly extends interface 'AccordionProps'.
Types of property 'children' are incompatible.
Type 'ReactChild[] | ReactNode' is not assignable to type 'ReactChild | undefined'.
Type 'null' is not assignable to type 'ReactChild | undefined'.ts(2430)

如果你能帮助它接受多个孩子,我将非常感激……

提前谢谢你

编辑:我用

更新了类型
type MultipleChildrenAccordion = Omit<AccordionProps, 'children'> & {
children?: React.ReactChild[]|React.ReactNode|undefined;
}

但是现在在<StyledAccordion onChange={onChange}>上我得到:

No overload matches this call.
Overload 1 of 2, '(props: { onChange?: ((accordion: AccordionAPI, openItems: number[]) => void) | undefined; ref?: Ref<AccordionAPI> | undefined; key?: Key | null | undefined; ... 8 more ...; iconProps?: IconProps | undefined; } & { ...; } & { ...; }): ReactElement<...>', gave the following error.
Type '{ children: Element[]; onChange: (() => void) | undefined; }' is not assignable to type '{ onChange?: ((accordion: AccordionAPI, openItems: number[]) => void) | undefined; ref?: Ref<AccordionAPI> | undefined; key?: Key | null | undefined; ... 8 more ...; iconProps?: IconProps | undefined; }'.
Types of property 'children' are incompatible.
Type 'Element[]' is not assignable to type '(ReactChild & (string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal | ReactChild[] | null)) | undefined'.
Type 'Element[]' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>> & ReactChild[]'.
Type 'Element[]' is missing the following properties from type 'ReactElement<any, string | JSXElementConstructor<any>>': type, props, key
Overload 2 of 2, '(props: StyledComponentPropsWithAs<ForwardRefExoticComponent<AccordionProps & RefAttributes<AccordionAPI>>, any, Omit<AccordionProps, "children"> & { ...; }, never, ForwardRefExoticComponent<...>, ForwardRefExoticComponent<...>>): ReactElement<...>', gave the following error.
Type '{ children: Element[]; onChange: (() => void) | undefined; }' is not assignable to type '{ onChange?: ((accordion: AccordionAPI, openItems: number[]) => void) | undefined; ref?: Ref<AccordionAPI> | undefined; key?: Key | null | undefined; ... 8 more ...; iconProps?: IconProps | undefined; }'.
Types of property 'children' are incompatible.
Type 'Element[]' is not assignable to type '(ReactChild & (string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal | ReactChild[] | null)) | undefined'.

当你扩展一个类型时,你只能使它变窄,而不能变宽。如果允许将它变宽,这基本上会破坏子类型。考虑这个例子:

interface Person {
name: string;
}
const someFunction = (person: Person) => {
console.log(person.name.toUpperCase()); // Should be just fine: name is a string, strings have a toUpperCase property
}
const example: Person = {
name: 'bob'
}
someFunction(example);

现在我想从人扩展。Typescript将允许我将其缩小,例如将名称限制为特定的字符串。这个扩展的类型可以传递给someFunction, someFunction仍然可以工作:

interface SpecificPerson extends Person {
name: 'bob' | 'alice' | 'eve',
}
const example2: SpecificPerson = {
name: 'alice'
}
someFunction(example2);

但是如果我被允许让事情变得更广泛,那么someFunction就会中断。name不再是字符串,所以当someFunction尝试调用.toUpperCase()时,将抛出一个异常

interface Entity extends Person {
name: string | number; // Typescript won't actually let me do this
}
const example3: Entity = {
name: 42,
}
someFunction(example3); // This shows a typescript error at compile time, and would throw a runtime error if it was allowed.

操场上联系


如果你想要一个类似于AccordionProps的类型,但它将子类型替换为更宽的类型,你可以这样做,但它不会从AccordionProps扩展。你将不能在期望AccordionProps:

的地方使用它
type MultipleChildrenAccordionProps = Omit<AccordionProps, 'children'> & {
children?: React.ReactChild[]|React.ReactNode|undefined;
}

注意Accordion不能使用这个类型。如果accordion被实现为只支持单个子节点,那么你必须给它一个单独的子节点。

根据Accordion是如何实现的,也许你可以给它传递一个片段:

<StyledAccordion onChange={onChange}>
<>
<dt>Question(SBTi)?</dt>
<div>answer </div>
<dt>Question(SBTi)?</dt>
<div>answer </div>
</>
</StyledAccordion>

如果不行,给它一个div:

<StyledAccordion onChange={onChange}>
<div>
<dt>Question(SBTi)?</dt>
<div>answer </div>
<dt>Question(SBTi)?</dt>
<div>answer </div>
</div>
</StyledAccordion>