React.ComponentType
React.ComponentType<P> is the type for either a class component (React.ComponentClass<P>) or function component (React.FunctionComponent<P> aka React.FC<P>) which takes props P.
(FYI, React.ReactNode and React.ReactElement are the types for the JSX returned by the component - not for a callable component)
Typing Comp's Props
You do not want to use <unknown> in your Props type. You want to declare that the component requires only the props which you are providing when you call React.createElement(Comp) (you call also use JSX and return <Comp/>). In this case you are providing no props, so it would be React.ComponentType<{}> or just React.ComponentType since {} is the default for P.
interface Props {
Comp: React.ComponentType;
}
const MyComp: React.FC<Props> = ({ Comp }) => {
return <Comp />
}
with some props:
interface Props {
Comp: React.ComponentType<{someKey: string}>;
}
const MyComp: React.FC<Props> = ({ Comp }) => {
return <Comp someKey="someValue" />
}
You will get an error if you call Comp without providing someKey, which is good! You don't get that error when calling React.createElement(Comp) because for some reason the props argument is optional. So in my opinion the JSX approach is better.
React.ComponentType
React.ComponentType<P> is the type for either a class component (React.ComponentClass<P>) or function component (React.FunctionComponent<P> aka React.FC<P>) which takes props P.
(FYI, React.ReactNode and React.ReactElement are the types for the JSX returned by the component - not for a callable component)
Typing Comp's Props
You do not want to use <unknown> in your Props type. You want to declare that the component requires only the props which you are providing when you call React.createElement(Comp) (you call also use JSX and return <Comp/>). In this case you are providing no props, so it would be React.ComponentType<{}> or just React.ComponentType since {} is the default for P.
interface Props {
Comp: React.ComponentType;
}
const MyComp: React.FC<Props> = ({ Comp }) => {
return <Comp />
}
with some props:
interface Props {
Comp: React.ComponentType<{someKey: string}>;
}
const MyComp: React.FC<Props> = ({ Comp }) => {
return <Comp someKey="someValue" />
}
You will get an error if you call Comp without providing someKey, which is good! You don't get that error when calling React.createElement(Comp) because for some reason the props argument is optional. So in my opinion the JSX approach is better.
What I use and see in many projects is this:
interface ComponentProps {
myComponentAsProp: () => React.ReactNode
}
const HomePage: React.FC<ComponentProps> = (props) => {
return (
<div>
<h3>{props.myComponentAsProp()}</h3>
</div>
);
};
To render it is like a function call.
Typescript React, best way to define component with props?
reactjs - TypeScript React: Access component property types - Stack Overflow
reactjs - React component type in TypeScript - Stack Overflow
React TypeScript HoC - passing Component as the prop
Videos
import React from "react";
interface FooterProps {
/* list of props */
}
export const Footer: React.FC<FooterProps> = ({}) => {
return <div>Footer</div>;
};
export const Footer2 = (props: FooterProps) => {
return <div>Footer</div>;
};Which one of them is a better way to define a component with props? And what exactly is the difference?
2019: noticed all answers above are quite outdated so here is a fresh one.
Lookup type
With newer TS versions you can use lookup types.
type ViewProps = View['props']
Despite being very convenient, that will only work with class components.
React.ComponentProps
The React typedefs ship with an utility to extract the type of the props from any component.
type ViewProps = React.ComponentProps<typeof View>
type InputProps = React.ComponentProps<'input'>
This is a bit more verbose, but unlike the type lookup solution:
- the developer intent is more clear
- this will work with BOTH functional components and class components
All this makes this solution the most future-proof one: if you decide to migrate from classes to hooks, you won't need to refactor any client code.
Starting with TypeScript 2.8, you can use conditional types, e.g. given:
interface MyComponentProps { bar: string; }
declare const MyComponent: React.Component<MyComponentProps>;
interface MyComponentClassProps { bar: string; }
declare const MyComponentClass: React.ComponentClass<MyComponentClassProps>;
interface MyStatelessComponentProps { bar: string; }
declare const MyStatelessComponent: React.StatelessComponent<MyStatelessComponentProps>;
We can define these helpers:
type GetComponentProps<T> = T extends React.ComponentType<infer P> | React.Component<infer P> ? P : never
And use them like so:
// $ExpectType MyComponentProps
type MyComponentPropsExtracted = GetComponentProps<typeof MyComponent>
// $ExpectType MyComponentClassProps
type MyComponentClassPropsExtracted = GetComponentProps<typeof MyComponentClass>
// $ExpectType MyStatelessComponentProps
type MyStatelessComponentPropsExtracted = GetComponentProps<typeof MyStatelessComponent>
Update 2018-12-31: this is now available in the official React typings via React.ComponentProps.
The correct type for a functional component is React.FunctionComponent or React.FC which is a shortcut alias for it
import React, { FC } from 'react';
const getTabContent: FC = () => {
switch (tab) {
case 1:
return <Images images={images} onSelect={onSelect}/>;
default:
return <Search onSelect={onSelect}/>;
}
};
The FC type simply add the children property to the props argument of the functional component so you can access it:
const SomeComponent: FC = ({ children }) => (
<div className="hello">{children}</div>
);
FC is a generic type so you can "add" props to your component:
interface SomeComponentProps {
foo: string;
}
const SomeComponent: FC<SomeComponentProps> = ({ children, foo }) => (
<div className={`Hello ${foo}`}>{children}</div>
);
Edit: React 18 update
Since React 18, FC doesn't add the children prop implicitly and offers an explicit way to do so with the PropsWithChildren generix type
Example:
type SomeComponentProps = { a: string };
const SomeComponent: FC<SomeComponentProps> = ({ a }) => <div>{a}</div>;
// This will fail when using the following expression
<SomeComponent>Hey I'm a child</SomeComponent>
Usage with children:
type ComponentWithChildrenProps = PropsWithChildren<{ a: string }>;
const ComponentWithChildrenProps: FC<ComponentWithChildrenProps> = ({
a,
children
}) => <div>{a} and {children}</div>
This allows to have a children prop a bit stricter. e.g.
type StrictCompProps = { children: string };
const StrictComp: FC<StrictCompProps> = ({ children }) => <div>{children}</div>;
// This will fail
<StrictComp><span>hey</span></StrictComp>
If you want to use FunctionComponent with class Component,
Then use React.ComponentType
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} />
After a few hours and some investigation, here is the solution that fits my requirements:
import * as React from 'react';
import { Route, RouteComponentProps, RouteProps } from 'react-router';
const PrivateRoute: React.SFC<RouteProps> =
({ component: Component, ...rest }) => {
if (!Component) {
return null;
}
return (
<Route
{...rest}
render={(props: RouteComponentProps<{}>) => <Component {...props} />}
/>
);
};
export default PrivateRoute;
- No
any; - No extra complexity;
- Composition pattern retained;
Hi,all.
I want to defined a Link component that has an as props. For example, using react router link component. <Link as={ReactRouterLink} to="/">Page</> . In this case, even I know the types of react router link, for example to props, but I don't want to define it on the Link. Is there a way to define the types of Link that can extend the type from as component?