Can I infer TypeScript type from a PropTypes shape?

132 Views Asked by At

I know how to infer types in this case:

import PropTypes from 'prop-types';

const props = {
  id: PropTypes.number,
};

type Props = PropTypes.InferProps<typeof props>;

const x: Props = {};
x.id; // number | null | undefined

However, in my case I have

const propsShape = PropTypes.shape({
  id: PropTypes.number,
  // more properties including nested PropTypes.shape calls
});

If I try to

type PropsFromShape = PropTypes.InferProps<typeof propsShape>;
const y: PropsFromShape = {};
const z = y.id;

it doesn't compile:

Type '{}' is not assignable to type 'PropsFromShape'.
  Property 'isRequired' is missing in type '{}' but required in type 'InferPropsInner<Pick<Requireable<InferProps<{ id: Requireable<number>; }>>, "isRequired">>'.

Property 'id' does not exist on type 'PropsFromShape'.

I could extract the argument of shape into a separate constant and work as above, but is there a nice way to infer the property types directly from propsShape?

1

There are 1 best solutions below

0
Lesiak On BEST ANSWER

To get the type of the nested object, you can use type NestedProps = PropTypes.InferProps<typeof propsShape>['isRequired'];

import PropTypes from "prop-types";

const propsShape = PropTypes.shape({
  nestedId: PropTypes.number,
  // more properties including nested PropTypes.shape calls
});

const props = {
  id: PropTypes.number,
  optionalWithShape: propsShape
};

type Props = PropTypes.InferProps<typeof props>;
type NestedProps = PropTypes.InferProps<typeof propsShape>['isRequired'];

const x: Props = {};
x.id = 1;

const y: NestedProps = {
  nestedId: 1
}

x.optionalWithShape = y;

Alternatively, if you can have entire props definition in one place:

import PropTypes from "prop-types";

const props = {
  id: PropTypes.number,
  optionalWithShape: PropTypes.shape({
    nestedId: PropTypes.number
  })
};

type Props = PropTypes.InferProps<typeof props>;
type NestedProps = Props['optionalWithShape'];

const x: Props = {};
x.id = 1;

const y: NestedProps = {
  nestedId: 1
}

x.optionalWithShape = y;

console.log(x.optionalWithShape.nestedId);

The latter reads better IMHO.