TypeScript Generics in React Components.
Source: Dev.to
Introduction
Generics aren’t something you use in React components every day, but in certain cases they let you write components that are both flexible and type‑safe.
Basic Component Definitions
Without Props
// Just FC component with no props
const Component = () => Hey ya!!!;
With Props using React.FC
// FC component with props
interface Props {
name: string;
}
const Component: React.FC = ({ name }) => (
Hey {name} ya!!!
);
React.FChas fallen out of favor for a few reasons, one being that it doesn’t handle generics the way we sometimes need. Still, many developers keep using it for readability.
Without React.FC
// FC component with simple props
interface Props {
name: string;
}
const Component = ({ name }: Props) => Hey {name} ya!!!;
When Generics Are Needed
Consider a situation where a component receives a DTO (Data Transfer Object) with an array from an API. The component needs to display a property that represents a “name”, but the exact property name varies between different DTOs.
Example DTO
interface ResponseDTO {
id: number;
name: string;
}
Component Tied to a Specific DTO
interface ExampleProps {
data: ResponseDTO[];
getName: (item: ResponseDTO) => string;
}
export const Example = ({
data,
getName = (item) => item.name,
}: ExampleProps) => (
{data.map((item) => (
{getName(item)}
))}
);
This works because the component knows the exact shape of ResponseDTO. If we want the component to handle any DTO that has a “name‑like” value, we need generics.
Generic Props Interface
interface Props {
data: T[];
getName: (item: T) => string;
}
The generic parameter T ties the data array and the getName callback together, ensuring they operate on the same type.
Generic Component
export const NameViewer = ({ data, getName }: Props) => (
<>
Look at the names:
{data.map((item) => (
{getName(item)}
))}
);
Note: In
.tsxfiles, writing “ (with a trailing comma) disambiguates the generic declaration from JSX syntax.
Using the Generic Component
const unknownTypeData = [
{ id: 1, nameProp: 'John' },
{ id: 2, nameProp: 'Jane' },
];
const anotherUnknownTypeData = ['Foo', 'Bar'];
export const BusinessComponent = () => (
item.nameProp}
/>
item}
/>
);
NameViewer can now accept any type of data as long as a suitable getName function is provided.
Conclusion
This simple example demonstrates how generics can make a React component adaptable to various data shapes while preserving type safety. In future articles we’ll explore more advanced patterns such as conditional generics.