Keywords: TypeScript | React | Component Type Declaration
Abstract: This article explores the correct methods for declaring React component types in TypeScript. By analyzing core types such as ReactElement, React.FC, and React.FunctionComponent, it details best practices for typing functional components. It covers changes in the FC type before and after React 18, particularly the handling of the children prop, and introduces supplementary types like PropsWithChildren and ComponentType. Through refactored code examples, it demonstrates step-by-step how to add props type constraints to components, ensuring type safety and code maintainability.
Fundamental Concepts of React Component Type Declaration
In TypeScript development with React, correctly declaring component types is crucial for ensuring type safety. Developers often face confusion when choosing between ReactElement, React.FunctionComponent (abbreviated as FC), or other types. Taking the function from the Q&A as an example, the original code declares the return type as ReactElement, which is technically feasible since React functional components do return ReactElement. However, this approach overlooks the component's own type information, potentially leading to insufficient type checking.
Using React.FC to Declare Functional Components
React.FC is an alias for React.FunctionComponent, specifically designed for declaring functional component types. Refactoring the original code provides a more precise expression of the component type:
import React, { FC } from 'react';
const getTabContent: FC = () => {
switch (tab) {
case 1:
return <Images images={images} onSelect={onSelect} />;
default:
return <Search onSelect={onSelect} />;
}
};Using the FC type not only clarifies that this is a functional component but also automatically adds type inference for the props parameter. Before React 18, the FC type implicitly included the children property, allowing components to receive child elements. For example:
const SomeComponent: FC = ({ children }) => (
<div className="hello">{children}</div>
);Adding Props Type Constraints to Components
FC is a generic type that can add specific constraints to component props through type parameters. Define an interface to describe the props structure, then pass it as a generic parameter to FC:
interface SomeComponentProps {
foo: string;
}
const SomeComponent: FC<SomeComponentProps> = ({ children, foo }) => (
<div className={`Hello ${foo}`}>{children}</div>
);This method ensures type safety for props when the component is used, with TypeScript checking at compile time whether the passed props conform to the interface definition.
Changes in the FC Type in React 18
React 18 introduced significant adjustments to the FC type: it no longer implicitly adds the children property. This means that if a component needs to receive child elements, it must declare them explicitly. For instance, the following code will error in React 18 because children is not included in the props type:
type SomeComponentProps = { a: string };
const SomeComponent: FC<SomeComponentProps> = ({ a }) => <div>{a}</div>;
// Error: Component does not declare children property
<SomeComponent>Hey I'm a child</SomeComponent>To address this, React provides the PropsWithChildren generic type for explicitly including the children property:
type ComponentWithChildrenProps = PropsWithChildren<{ a: string }>;
const ComponentWithChildren: FC<ComponentWithChildrenProps> = ({
a,
children
}) => <div>{a} and {children}</div>;Additionally, developers can specify stricter types for children, such as restricting it to strings:
type StrictCompProps = { children: string };
const StrictComp: FC<StrictCompProps> = ({ children }) => <div>{children}</div>;
// Error: children must be a string, not a React element
<StrictComp><span>hey</span></StrictComp>Supplementary Type: Application of ComponentType
Beyond FC, React also offers the ComponentType type, which supports both functional and class components. This is useful in scenarios requiring handling of multiple component forms, such as higher-order components or dynamic loading. Although the second answer in the Q&A mentions this type, FC is generally a more precise choice for functional component contexts.
Summary and Best Practice Recommendations
When declaring React component types in TypeScript, it is recommended to use React.FC or React.FunctionComponent as the standard type for functional components. For components requiring props constraints, pass interface definitions via generic parameters. In React 18 and above, use PropsWithChildren to explicitly handle the children property and specify its type as needed. Avoid directly using ReactElement as the component return type, as it lacks component-level type information. By following these practices, you can enhance code type safety, readability, and maintainability, reducing runtime errors.