React TypeScript HoC – passing Component as the prop

You want to pass a component constructor, not a component instance:

import * as React from 'react';
import { Route, RouteProps } from 'react-router';

interface Props extends RouteProps {
    component: React.ComponentType;
}

const PrivateRoute = ({ component: Component, ...rest }: Props) => {
    return (
        <Route
            {...rest}
            render={(props) => <Component {...props} />}
        />
    );
};

export default PrivateRoute;

class Foo extends React.Component {

}
let r = <PrivateRoute component={Foo} path="/foo" />

Edit

A more complete solution should be generic and use RouteProps instead RouterProps:

import * as React from 'react';
import { Route, RouteProps } from 'react-router';

type Props<P> =  RouteProps & P & {
    component: React.ComponentType<P>;
}

const PrivateRoute = function <P>(p: Props<P>) {
    // We can't use destructuring syntax, because : "Rest types may only be created from object types", so we do it manually.
    let rest = omit(p, "component");
    let Component = p.component;
    return (
        <Route
            {...rest}
            render={(props: P) => <p.component {...props} />}
        />
    );
};

// Helpers
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];  
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>; 
function omit<T, TKey extends keyof T>(value:T, ... toRemove: TKey[]): Omit<T, TKey>{
    var result = Object.assign({}, value);
    for(let key of toRemove){
        delete result[key];
    }
    return result;
}


export default PrivateRoute;

class Foo extends React.Component<{ prop: number }>{

}
let r = <PrivateRoute component={Foo} path="/foo" prop={10} />

Leave a Comment