reactjstypescriptchildrenreact-tsx

ReactTS : How to nest children in a "container component"?


I am working on creation of reusable front components in React using Typescript and I am struggling with a feature which seems to be possible in classic packages.

I would like to be able to nest children of a certain type in "container components" and retrieve each sets of children in the main component.

I tried a lot of variants in the type of children props such as:

export type DataGridChild = typeof DataGridColumns | typeof DataGridActions;

export type DataGridProps<T> = BaseProps & {
    data: T[];
    children: React.ReactElement<DataGridChild> | React.ReactElement<DataGridChild>[];
}

But then I can't find a way to retrieve each child with the right type. Also TypeScript does not throw any error if I add a DataGridColumn container component twice for instance.

In the end I would like to have a component usable with this kind of code:

<DataGrid data={persons}>
  <DataGridColumns>
    <DataGridColumn field="name" />
    <DataGridColumn field="age" />
    <DataGridColumn field="weight" />
    <DataGridColumn field="birthDate" type="date" />
  </DataGridColumns>
  <DataGridActions>
    <DataGridAction text="Action 1" onClick={(rd) => console.log("Action 1 clicked")} />
  </DataGridActions>
</DataGrid>

I can't find anything on the web. Does anybody has an idea on how to make it?

Thanks a lot :)


Solution

  • Since you are using Unions, you will ultimately do that which the so called classic packages do: Union narrowing. See more at Working with Union Types. [typescriptlang.org]


    Let’s break down the code. You declared a type alias called DataGridChild for the Union:

    typeof DataGridColumns | typeof DataGridActions

    So that anywhere a DataGridChild is used, a typeof DataGridColumns or a typeof DataGridActions is expected. Since that’s the choice, expect to perform narrowing.

    Next you declared a type alias DataGridProps<T> for the anonymous type built by extending a type BaseProps and including two properties:

    BaseProps & {
        data: T[];
        children: React.ReactElement<DataGridChild> | React.ReactElement<DataGridChild>[];
    }

    With the explanations now out of the way, you should be able to answer your questions.

    For posterity however, let’s answer them together.

    I can't find a way to retrieve each child with the right type.

    Perform Union narrowing.

    Also TypeScript does not throw any error if I add a DataGridColumn container component twice for instance.

    You only defined a type alias DataGridProps<T> which I suspect you used to declare and define the prop of element DataGrid.

    If you didn’t define a prop Interface for neither DataGridColumns nor DataGridActions, then their props are implicitly any. Because of this, TypeScript will not provide type checking hence no type checking errors. Define an Interface for the components to provide finer control and type safety for the components.