Asaf Aviv
Asaf Aviv

Reputation: 11770

How to write a generic that extracts the prop types of the component that passed in

How can i write a generic that takes in a component and use its prop type as the type of the second parameter

Lets say i pass in a component with the type of React.FunctionComponent<IMovieShowcase>

How can i extract the type of IMovieShowcase

function renderWithProviders<T How to extract type of T<Props>>(
  Component: T, props: // props should be IMovieShowcase
) { 
  return (
    <MemoryRouter>
      <MuiThemeProvider theme={createMuiTheme()}>
        <Component {...props} />
      </MuiThemeProvider>
    </MemoryRouter>
  );
}

Upvotes: 1

Views: 1130

Answers (1)

skovy
skovy

Reputation: 5650

We can use TypeScript's type inference in conditional types.

This InferProps accepts the generic named Component. So it can be used as InferProps<YourComponent> to return the type of the props. As far as I am aware of there is the ComponentClass and FunctionComponent types that are valid React components. Since we have to handle both we can use a nested conditional (hence the double ?).

The first conditional statement Component extends React.ComponentClass<infer Props> is saying if our Component extends a React.ComponentClass then infer the Props and return that type. If it's not, then check Component extends React.FunctionComponent<infer Props>. If Component extends a React.FunctionComponent then infer the Props and return that type. Otherwise, return never because we're not sure how to handle or what it is so we can't infer the props.

type InferProps<
  Component extends ComponentTypes
> = Component extends React.ComponentClass<infer Props>
  ? Props
  : Component extends React.FunctionComponent<infer Props>
  ? Props
  : never;

Used in a simplified example with the code you provided:

import * as React from "react";

type ComponentTypes = React.ComponentClass<any> | React.FunctionComponent<any>;

type InferProps<
  Component extends ComponentTypes
> = Component extends React.ComponentClass<infer Props>
  ? Props
  : Component extends React.FunctionComponent<infer Props>
  ? Props
  : never;

function renderWithProviders<T extends ComponentTypes>(
  Component: T,
  props: InferProps<T>
) {
  return <Component {...props} />;
}

class Test extends React.Component<{ foo: string }> {
  render() {
    return null;
  }
}

const Another = (props: { baz: number }) => null;

// Valid:
renderWithProviders(Test, { foo: "bar" });

// Valid:
renderWithProviders(Another, { baz: 1 });

// Invalid:
//  Object literal may only specify known properties,
//  and 'baz' does not exist in type '{ foo: string; }'
renderWithProviders(Test, { foo: "bar", baz: "foo" });

// Invalid:
//  The expected type comes from property 'baz' which is
//  declared here on type '{ baz: number; }'
renderWithProviders(Another, { baz: "nope" });

Edit infer-props

Upvotes: 3

Related Questions