8

We have some complex compositions of components, and I'm having trouble making them typesafe. Based on the code below, I'd have expected Typescript to be able to provide types for Menu.Item or Menu.Link, but it doesn't for some reason, they're inferred as "any" (which is refined to JSX.Element<any> when you wrap it in JSX. Any idea why this is happening?

import * as React from 'react';

type RootFunctionComponent<OwnProps, SubComponents> = React.FunctionComponent<OwnProps> & SubComponents;

interface ItemProps {
    text: string;
};

interface LinkProps {
    href: string;
}

const Item: React.FunctionComponent<ItemProps> = props => <div {...props} />;
const Link: React.FunctionComponent<LinkProps> = props => <div {...props} />;

interface MenuSubComponents {
    Item: React.FunctionComponent<;
    Link: typeof Link;
}

const Menu: React.FunctionComponent<{}, MenuSubComponents> & MenuSubComponents = props => <div {...props} />

Menu.Item = Item;
Menu.Link = Link;


const Test: React.FunctionComponent<{}> = () => {
    return <>
        <Menu>
            <Menu.Item text={false} />
            <Menu.Link />
        </Menu>
    </>
}

Result:

the resulting any type

1
  • How about fixing your example now? :) Commented Dec 3, 2020 at 8:20

2 Answers 2

10

The issue was with the menu type definitions:

React.FC<{}, MenuSubComponents>

Correct usage is:

React.FC<MenuSubComponents>

You can see this in action here: https://codesandbox.io/s/condescending-lamarr-xjkcw

Sign up to request clarification or add additional context in comments.

1 Comment

This doesn't constrain the children of Menu to be what you specified for the sub-components though. You can still specify any kind of react child for Menu.
3

Here is Full Example of A Higher Order Component Table with props

import React from 'react'
import { tw } from 'twind'

import Body from './Body'
import Cell from './Cell'
import Head from './Head'
import Row from './Row'

export type CommonPropsType = {
  className?: string
  children: React.ReactNode
}

type TableSubComponents = {
  Row: typeof Row
  Cell: typeof Cell
  Head: typeof Head
  Body: typeof Body
}

type TablePropsType = CommonPropsType & { wrapperClassName?: string }

const Table: React.FunctionComponent<TablePropsType> & TableSubComponents = (
  props: TablePropsType
) => {
  const { className, children, wrapperClassName } = props
  return (
    <div className={tw('overflow-x-auto pb-2', wrapperClassName)}>
      <table
        className={tw('min-w-full leading-normal', className)}
        cellPadding="8">
        {children}
      </table>
    </div>
  )
}

Table.Row = Row
Table.Cell = Cell
Table.Head = Head
Table.Body = Body

export default Table

1 Comment

I don't suppose you know the TypeScript to get this working when using React.forwardRef with the component? Can't for the life of my figure out what this might be, and consequently, I've had to disable the sub-components.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.