Skip to content

Instantly share code, notes, and snippets.

@caprica
Created November 20, 2022 07:38
Show Gist options
  • Save caprica/554648586f9e35a204f82f0c281d1097 to your computer and use it in GitHub Desktop.
Save caprica/554648586f9e35a204f82f0c281d1097 to your computer and use it in GitHub Desktop.
Example of a React component using Typescript with generics
/*
* An outline example of how to create a generic component using Typescript and
* React.
*
* This is NOT the only way to do something like this.
*/
// === Generic component ===
/**
* All items in a Carousel must have a (unique) id.
*
* This interface is extended by concrete types.
*/
export interface CarouselItem {
id: string
}
/**
* A Carousel component has a model containing an array of any type of Carousel
* Item and a component to render an individual item.
*/
export interface CarouselProps<T extends CarouselItem> {
model: Array<T>
itemComponent: React.FC<CarouselItemProps<T>>
}
/**
* A Carousel Item component has a model of any type of single Carousel Item.
*/
export interface CarouselItemProps<T extends CarouselItem> {
model: T
}
/**
* A generic Carousel component.
*
* @param param0 component properties
* @returns component
*/
export const Carousel = <T extends CarouselItem>({ model, itemComponent: ItemComponent }: CarouselProps<T>) => {
return (
<div>
{model.map(item => (
<div key={item.id}>
<ItemComponent model={item} />
</div>
))}
</div>
)
}
// === Custom component example ===
interface MyModel extends CarouselItem {
name: string
}
const MyTile = ({ model }: CarouselItemProps<MyModel>) => {
return <div>{model.name}</div>
}
export const MyTestComponent = () => {
const model: MyModel[] = [
{
id: '1',
name: 'One'
},
{
id: '2',
name: 'Two'
}
]
/*
* You could declare the model type on the Carousel component, but
* Typescript can infer it from the model property.
*/
// return <Carousel<MyModel> model={model} tileComponent={MyTile} />
return <Carousel model={model} itemComponent={MyTile} />
}
// === Another custom component example ===
interface MyOtherModel extends CarouselItem {
title: string
description?: string
tags?: string[]
price: number
}
const MyOtherTile = ({ model }: CarouselItemProps<MyOtherModel>) => {
return (
<div>
<h1>{model.title}</h1>
<h2>{model.description}</h2>
{model.tags && (
<ul>
{model.tags.map(tag => (
<li key={tag}>{tag}</li>
))}
</ul>
)}
<span>£{model.price}</span>
</div>
)
}
export const MyOtherTestComponent = () => {
const model: MyOtherModel[] = [
{
id: '1',
title: 'One',
description: 'Hey hey',
price: 100
},
{
id: '2',
title: 'Two',
tags: ['A', 'B', 'C'],
price: 250
}
]
/*
* You could declare the model type on the Carousel component, but
* Typescript can infer it from the model property.
*/
// return <Carousel<MyOtherModel> model={model} tileComponent={MyOtherTile} />
return <Carousel model={model} itemComponent={MyOtherTile} />
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment