Development

TypeScript Generics Explained - Creating Reusable Components

How do generics work in TypeScript? Learn how you can leverage TypeScript generics to create reusable components across a variety of types within your JavaScript application.

4 min
June 10, 2022
Chris Held
Development Lead

TypeScript is a great way to improve consistency and reliability in your JavaScript applications, as well as improve developer experience with static analysis tools.

### The problem

However, this does carry some overhead, and eventually, your application might start to carry a lot of types that can feel overwhelming.

### The solution

Thankfully, TypeScript has a few ways to refactor and reuse common types, and the one we’re going to cover today is generics.

## What are TypeScript generics?
A TS generic is a concept borrowed from other more strongly typed languages like C# and Java, and it allows a component to work across a variety of types, rather than a single one.


Here is a simple example of how that works in TypeScript:


   -- CODE line-numbers language-jsx --

   <!--

     function identity<T>(arg: T): T {

       return arg;

     }


     // output is typed as a string so returns a string

     let output = identity<string>("foo");

   -->


## Typescript generics examples

You can watch the video below or walk through the examples in this article at your own pace.


Let’s say you have a standard front-end application where users log in and perform actions on different things that are important to their business.

In this example, we’ll say those things are _Users_ and _Companies_.

Both _Users_ and _Companies_ are loaded into respective tables for viewing.

The types for that might look like this:


   -- CODE line-numbers language-jsx --

   <!--

     type UserListOptions = {

       pageSize: number;

       page: number;

       orderBy: 'name' | 'age';

       sortDirection: 'ASC' | 'DESC'

     };


     type UserListResult = {

       items: User[];

       page: number;

     };


     type CompanyListOptions = {

       pageSize: number;

       page: number;

       orderBy: 'name' | 'state';

       sortDirection: 'ASC' | 'DESC'

     };


     type CompanyListResult = {

       items: Company[];

       page: number;

     };


     // and on and on and on...

   -->


### There is an easy-to-spot pattern here

Each thing has a ListResult and ListOptions type that are almost identical.

With generics, we can refactor this concept into one type that will reduce a lot of copying and pasting:


   -- CODE line-numbers language-jsx --

   <!--

     type ListResult<T> = {

       items: Array<T>;

       page: number;

     }


     type ListOptions<T> = {

       pageSize: number;

       page: number;

       orderBy: T;

       sortDirection: 'ASC' | 'DESC'

     }


     type UserListResult = ListResult<User>;


     type UserListOptions = ListOptions<'name' | 'age'>;


     type CompanyListResult = ListResult<Company>;


     type CompanyListOptions = ListOptions<'name' | 'state'>;

   -->


### A short caveat on T

This is just a variable and can be named whatever makes sense to you, T is just a common standard that you will see a lot in documentation regarding generics.

The following code is just as valid and in this case makes the type much easier to read:


   -- CODE line-numbers language-jsx --

   <!--

     type ListOptions<OrderByOptions> = {

       pageSize: number;

       page: number;

       orderBy: OrderByOptions;

       sortDirection: 'ASC' | 'DESC'

     }

   -->


If you need multiple generics in a type you can comma-separate them:


   -- CODE line-numbers language-jsx --

   <!--

     type ListOptions<OrderByOptions, FilterByOptions> = {

       pageSize: number;

       page: number;

       orderBy: OrderByOptions;

       filterBy: FilterByOptions;

       filterByQuery: string;

       sortDirection: 'ASC' | 'DESC'

     }

   -->


If our types start to diverge we still have all the same options as before when using generics. Let's say we add a feature to deactivate users and want to add a filter for that in our UserListOptions.

We can create a type intersection while still abstracting away all of our common properties:


   -- CODE line-numbers language-jsx --

   <!--

     type UserListOptions = ListOptions<'name' | 'age'> & {isActive: boolean};

   -->


## More TypeScript resources

### Typescript Generics Docs

If you're interested in learning more about generics in TypeScript, check out the official docs here.

TypeScript Handbook - Generics

### TypeScript Course

If you're new and looking for a good course to learn TypeScript, Execute Program has a great set of courses here.

TypeScript Course - Execute Program

### Exploring Advanced TypeScript - Video

Explore some advanced TypeScript concepts such as utility functions and guards. If you're an adventurous JavaScript developer, you might find some value in this discussion too. Otherwise, it is the best fit for folks with some familiarity with TypeScript.

Advanced TypeScript Video


Actionable UX audit kit

  • Guide with Checklist
  • UX Audit Template for Figma
  • UX Audit Report Template for Figma
  • Walkthrough Video
By filling out this form you agree to receive our super helpful design newsletter and announcements from the Headway design crew.

Create better products in just 10 minutes per week

Learn how to launch and grow products less chaos.

See what our crew shares inside our private slack channels to stay on top of industry trends.

By filling out this form you agree to receive a super helpful weekly newsletter and announcements from the Headway crew.