Check types of components passed as props in Typescript React

Viewed 136

I have a question related to type checking in React components passed as props: How can we do type checking to ensure we are passing as props only allowed components?

Let me illustrate it. We have the component we want to pass around:

type DummyProps = {
  name: string;
};

const Dummy = ({ name }: DummyProps) => {
  return (<div>{name}</div>);
}

And the component we want to inject the Dummy component to (and have static type checking, so we cannot pass any other one):

type DummyWrapperProps = {
  dummy: ReactComponentElement<typeof Dummy>;
};

const DummyWrapper = ({ dummy }: DummyProps) => {
  return (<div>{name}</div>);
}

Now, if we call DummyWrapper, despite having explictly set the type of its dummy prop to ReactComponentElement<typeof Dummy>, it seems we can pass any ReactElement no matter what props/type it has:

const dummyElement = <Dummy name='dummy' />
const otherElement = <Other /> //this component is defined elsewhere

<DummyWrapper dummy={dummyElement} /> // This works
<DummyWrapper dummy={otherElement} /> // This also works

I have also tried to do similar things having DummyWrapperProps being defined with React.ReactElement, React.FC etc. but always get the same results: typescript static type checking does not warn about components/elements being passed not matching expected type.

I assume the issue also extends to children because it's basically the same thing: you are passing around elements.

Lastly, I know I could do some kind of dynamic type checking, reading properties of the node the component is receiving, but as I say I want to do type checking on compilation time.

1 Answers

I think, you can not do this in that way, because any element instantiation type will be JSX.Element and probably you can do this instead:

const DummyWraper = ({ dummy: D, name }: { dummy: FC<DummyProps> } & DummyProps) => (
  <div>
    <D name={name} />
  </div>
)

Resulting:

type DummyProps = {
  name: string
}

const Other: FC<{ some: number }> = () => <div>1</div>

const Dummy: FC<DummyProps> = ({ name }) => {
  return <div>{name}</div>
}

const dummyElement = <Dummy name="dummy" />
const otherElement = <Other />

const b = <DummyWraper dummy={Other} name="dummy" />
const c = <DummyWraper dummy={Dummy} name="dummy" />

Beware of FC<{}> type for Other because {} in typescript works like any for objects

Related