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,
});