DEV Community

Cover image for Use TypeScript Generics to enhance your React components and make them reusable
Nitsan Cohen
Nitsan Cohen

Posted on • Updated on

Use TypeScript Generics to enhance your React components and make them reusable

Use TypeScript Generics to enhance your React components and make them reusable!

What is TypeScript Generics?

Generics allow us to have a dynamic type for our components. It gives us more flexibility when building components and allows better reusability for the consumers of the components.

Let's have a look at a simple example of Generics:

function returnWhatItGets<Type>(arg: Type): Type => arg;
Enter fullscreen mode Exit fullscreen mode

The part where we write < Type> tells typescript that when we use this function, we will be able to insert a dynamic type that will serve as the Type of our argument (arg) and the Type of the return value of this function.

That is how it looks:

const numberOutput = returnWhatItGets<number>(5);

const stringOutput = returnWhatItGets<string>("hi");
Enter fullscreen mode Exit fullscreen mode

Now that we're all on the same page with Generics, let's implement generics in our React component!

1 import React from 'react';
2
3 export type GenericComponentProps<Type> = {
4 data: Type[];
5 };
6
7 export function GenericComponent<Type>({ data }: 8GenericComponentProps<Type>) {
9 return (
10 <>
11 {data?.map((item: Type, index: number) => (
12 <ul key={index}>
13 {(Object.keys(data[0]) as Array<keyof Type>).map((key) => (
14 <li key={key.toString()}>{item[key]}</li>
15 ))}
16 </ul>
17 ))}
</>
);
}
Enter fullscreen mode Exit fullscreen mode

It might not look very clear at first glance, but it's really straightforward. Let's go through the code line by line:

On line number 3, we define a Generic type for our component's props (Type).

In line 4, we assume that our props object will have the "data" property. Our data type will be an array of the dynamic Type we will initiate our component.

Moving forward to line 7, we define our functional component with the generic Type that will be passed down to the props object's Type.

Our data is an array with our custom Type, so in line 11, we begin mapping that array. For each object, we output an<ul> attribute.

Now we want to output all the properties values of the given object, so we turn our object to an array of its keys using the Object.keys method and map it to print the value for each property. Notice how we define the type for this array dynamically with the keyof syntax.

Now let's test our component:

  <GenericComponent
   data={[
    { name: 'Nitsan', city: 'Harish' },
    { name: 'Abraham', city: 'Beer Sheva' },
   ]}
  />

Enter fullscreen mode Exit fullscreen mode

That's it! We get all the benefits of typescript, such as auto-completion and type checking, while creating flexible and reusable components.

Using tools to keep track and build independent components, such as Bit, our users benefit significantly from such a flexible setup.


  • For more posts like this follow me on LinkedIn

  • I work as frontend & content developer for Bit - a toolchain for component-driven development (Forget monolithic apps and distribute to component-driven software).

Discussion (6)

Collapse
cmarker profile image
Christian 🇳🇴

But why?

Collapse
nitsancohen770 profile image
Nitsan Cohen Author

Isn't the example above obvious enough?
We receive a data prop, and we want to have it typed to receive all the benefits of typescript(auto-completion, etc.). If we hardcode the data, we limit our component to only one data structure. Using generics allows users to install our component and use any data structure they prefer.

Collapse
fjones profile image
FJones • Edited on
  1. Auto-completion is not a TypeScript feature. TS can help, but most of the auto-completion relevant for components isn't really helped by a generic component. Especially if you're just establishing a data prop.
  2. The code has so many flaws despite TypeScript, it's difficult to see how generics could help:
    1. Assuming data is an array of objects without mandating that.
    2. Getting the keys only from the first item of that array...
    3. ... but somehow assuming the key needs an explicit toString call and the value is renderable.
  3. The benefits of generics aren't actually shown here either. In fact, even the example at the end doesn't actually use a typed instance of the generic component.

Generics can be useful, if you're mandating a relationship between the types of different arguments/props. For example, say, mandating a { data: T[], render: (T) => Element } prop list.

Thread Thread
nitsancohen770 profile image
Nitsan Cohen Author • Edited on

Not going to answer all your comments, but regarding your claim that I am not using a typed instance, please read about Type Inference:

typescriptlang.org/docs/handbook/t...

Collapse
ayrbox profile image
ayrbox

I agree with you that typescript gives more benefit but at the time if writing GenericComponent data type is still unknonwn and at line 14 you are assuming that value is of type string. Besides saying that your technique is extremely use full if you are passing rendering list as child function. You can pass the data into rendering function where you will have all goodness of typescript (intellisense, compile time check and so on).

Hope its clear, let me know if you need an example.

Thread Thread
nitsancohen770 profile image
Nitsan Cohen Author

I am not sure I fully understood, please share your example. Thanks!