All Articles

Using Generics to Create Flexible Types in TypeScript

Joe PreviteJoe Previte

This article will explain generics in TypeScript using real-world examples and show you exactly how they’re used. We’ll even include TypeScript playground links for our examples.

This way, you can quickly try them yourself. By the end, you’ll know how to use generics like the pros.

What are Generics?

The best way for us to answer that is to start diving into an example. Let’s say you’re building an API called useState (like in React) that looks like this:

function useState(
  initialState: boolean
): [boolean, (newState: boolean) => void] {
  // implementation...
}

What if users want to use a different type for state, say a string? Well, we could modify our signature to take a union type.

function useState(
  initialState: boolean | string
): [boolean | string, (newState: boolean | string) => void] {
  // implementation...
}

Better, but what if another user wants to use some type of object? Wouldn’t it be nice if we could use something more generic? Ah-ha! This is where generics shine. Let’s refactor one last time.

function useState<State>(
  initialState: State
): [State, (newState: State) => void] {
  // implementation...
}

As you can see here, we use <Type> after the function name, kinda like defining an arg to a function, and then we can reference it throughout the rest of the signature! Now when we use this function, TypeScript will infer the type for State based on what we pass in!

screenshot of useState third versionLoading

Woohoo! Generics allow us to write more flexible functions, which is good for us and good for our end users. Let’s dive into some real examples from open source.

Show me the code

If you want to jump straight into code examples, take a look at these TypeScript playground links:

Examples of Generics in Open Source

Continuing with React, let’s look at the type definition for PropsWithChildren:

type PropsWithChildren<P> = P & { children?: ReactNode | undefined };

First, if you’re not familiar, PropsWithChildren is a helper type that you can import from react to define types for your component props without having to manually type children. Very handy! Looking at this, we see one generic called P for Props. This returns a new type and adds an optional property called children, which is of type ReactNode or undefined.

And here’s how you use it:

import React, { PropsWithChildren } from "react";

export type RequirePermissionProps = PropsWithChildren<{
  isFeatureVisible: boolean;
}>;

export const RequirePermission = ({
  children,
  isFeatureVisible,
}: RequirePermissionProps) => {
  if (!isFeatureVisible) {
    return <div>nothing to see here</div>;
  } else {
    return children;
  }
};

Let’s look at one more example to solidify our understanding of generics.

Generics used in document.createElement()

When working with the DOM, there is a method on document called createElement() where you pass in a tag name to create an instance of said element. Here is the type definition:

createElement<K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K];

When you use this function, you get an autocomplete list of valid HTML tags in your editor:

screenshot of document.createElement showing autocompleteLoading

More common in libraries than applications

Based on my experience, you’ll find these more often in libraries than in applications you’re writing. Library authors need the flexibility that generics provide. It’s still important for you to know if you want to master TypeScript though.

Resources

For more information on function overloads, take a look at these resources:

Summary

We went from not knowing what union types are to breaking down real examples from large open source projects like Prettier and React. As you saw, union types are everywhere. Shipping production-ready TypeScript code means you need the fundamentals down. Now that you know how to use union types, you can combine types, which means less code to do more. More time to ship and less time maintaining bad code. Woohoo! Happy coding!

We learned about generics starting with a basic example inspired by useState in React, before moving on to reading source code from real open source projects and breaking down how they use generics to write flexible type definitions.

While generics are more common in libraries than applications, they’re a powerful concept in TypeScript, and one you should take advantage of when needed. Now that you have this knowledge, go out and use generics in your code. And I’ll leave you with this great one-liner to describe generics: