georges8899
georges8899

Reputation: 70

React TypeScript accepts missing props on ComponentType prop

I'm trying to ensure that the component passed to another component as a prop (i.e. <Foo Component={Bar} />) is declared with the correct props.

This works fine when the component being passed in has props defined. However, if there are no props defined, TypeScript doesn't throw an error.

Here's an example:

import { ComponentType } from 'react';

type A = {
  foo: string;
}

type B = {
  Component: ComponentType<A>;
}

const Test = ({ Component }: B) => (
  <Component foo="test" />
);

const NoProps = () => <div />;
const CorrectProps = ({ foo }) => <div />;
const IncorrectProps = ({ bar }) => <div />;

// does not error - but it should?
const C = () => <Test Component={NoProps} />;

// no error as expected
const D = () => <Test Component={CorrectProps} />;

// errors as expected
const E = () => <Test Component={IncorrectProps} />;

I was wondering if it is possible to enforce the correct props to be defined?

Upvotes: 0

Views: 1354

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 187064

This is more a quirk of functions.

In javascript it raises no errors to provide more arguments than a function accepts. And typescript agrees.

For example:

type Fn = (arg: number) => number
const fn: Fn = () => 123 // this is fine
fn(999) // 123

See playground

This feature is really handy and used a lot. For example in a react onClick callback:

onClick={() => doStuff()} // no arg
onClick={(event) => doStuffWithEvent(event)} // with arg

That works because typescript allows you pass a function of fewer arguments to a function type expects more arguments. And it's pretty nice that we don't get an error in the first case where the argument is omitted. In general, it's better to omit the argument than to declare it and not use it.


So since functional components are just functions, the same rules apply. Remember that the <Component> angle brace syntax is just syntax sugar for calling your functions.

So:

<Component foo='test' />

Will end up calling:

Component({ foo: 'test' })

And if Component happens to be a function type that accepts no arguments, then no type error is raised because nothing bad will happen.

The reason that you do get an error here:

// errors as expected
const E = () => <Test Component={IncorrectProps} />;

Is that now you are passing in a function that takes one argument, and the type of that argument is actually wrong. This means your component requires a prop that Test will not pass to it, which will likely cause a crash.

So, in general, I don't think there's any harm in allowing you to pass a component that takes no props, and Typescript seems to agree.

Upvotes: 1

Related Questions