Using dot notation with functional component in TypeScript

const Card: React.FunctionComponent & { Body: React.FunctionComponent } = ({ children }): JSX.Element => <>{children}</>
const Body: React.FunctionComponent = (): JSX.Element => <>Body</>

Card.Body = Body;

Or more readable:

type BodyComponent = React.FunctionComponent;
type CardComponent = React.FunctionComponent & { Body: BodyComponent };

const Card: CardComponent = ({ children }): JSX.Element => <>{children}</>;
const Body: BodyComponent = (): JSX.Element => <>Body</>;

Card.Body = Body;

I found a neat way using Object.assign to make dot notation work with ts. There were use cases similar to

type TableCompositionType = {
    Head: TableHeadComponentType;
    Body: TableBodyComponentType;
    Row: TableRowComponentType;
    Column: TableColumnComponentType;
};
type TableType = TableComponentType & TableCompositionType;


export const Table: TableType = TableComponent;
Table.Head = TableHeadComponent;
Table.Body = TableBodyComponent;
Table.Row = TableRowComponent;
Table.Column = TableColumnComponent;

where ts would throw errors. My basic working solution was:

export const Table: TableType = Object.assign(TableComponent, {
    Head: TableHeadComponent,
    Body: TableBodyComponent,
    Row: TableRowComponent,
    Column: TableColumnComponent,
});

The only drawback is that while the result will be typechecked, the individial subcomponents inside the object parameter wouldn't be, which might be helpful for debugging.

A good practice would be to define (and typecheck) the parameter beforehand.

const tableComposition: TableCompositionType = {
    Head: TableHeadComponent,
    Body: TableBodyComponent,
    Row: TableRowComponent,
    Column: TableColumnComponent,
};

export const Table: TableType = Object.assign(TableComponent, tableComposition);

But since Object.assign is generic, this is also valid:

export const Table = Object.assign<TableComponentType, TableCompositionType>(TableComponent, {
    Head: TableHeadComponent,
    Body: TableBodyComponent,
    Row: TableRowComponent,
    Column: TableColumnComponent,
});

Of course, if you don't need to (or want to) explicitly specify the type beforehand, you can also do that and it will just get inferred. No nasty hacks required.

export const Table = Object.assign(TableComponent, {
    Head: TableHeadComponent,
    Body: TableBodyComponent,
    Row: TableRowComponent,
    Column: TableColumnComponent,
});