Search code examples
reactjstypescriptreact-typescript

How to set a type for a React Compnent being passed in as a prop


I have the following code where I'm rendering a component or redirecting the user based on auth state. I'm having trouble understanding what the component prop type should be set to in the interface I've declared:

import { useState } from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useAuthState } from '../../../context/authenticationContext';

interface AppProps {
    path: string,
    isPrivate: boolean,
}

const AppRoutes = ({ component: Component, path, isPrivate, ...rest }: AppProps) => {
    const authDetails = useAuthState();
    ...
    return (
        <Route
            path={path}
            render={props => (isPrivate && !authDetails.user) ? (
                <Redirect
                    to={{ pathname: "/" }}
                />
            ) : (
                <Component {...props} />
            )
            }
            {...rest}
        />
    )
}

export default AppRoutes

As you can see my interface is missing the component type. Not sure what to set it to.


Solution

  • If you want to type component constructor you should use import {ComponentType} from 'react'.

    So, if you want to build Component with props from render function, you should apply a constraint for ComponentTypeProps.

    In other words, your Component should expect Route['render'] type of argument. In order to get type of render argument, you should use ComponentProps utility, which you can import from 'react'.

    COnsider this example:

    type RenderProps = Parameters<NonNullable<ComponentProps<typeof Route>['render']>>[0]
    
    

    Now you can use appropriate constraint:

    import React, { ComponentType, ComponentProps } from 'react';
    import { Route } from 'react-router-dom';
    
    interface AppProps {
      path: string,
      isPrivate: boolean,
      Component: ComponentType<RenderProps>
    }
    
    type RenderProps = Parameters<NonNullable<ComponentProps<typeof Route>['render']>>[0]
    
    const AppRoutes = ({ Component, path, isPrivate, ...rest }: AppProps) => {
    
      return (
        <Route
          path={path}
          render={props => <Component {...props} />}
          {...rest}
        />
      )
    }
    
    export default AppRoutes
    

    Playground