🛠️ 📦 TypeScript Generics - a cheat sheet
Audrey Kadjar

Audrey Kadjar @audreyk

About: I am a Software Engineer passionate about building software that brings value to its users. Excited to share my learnings here! 👩🏻‍💻 ✨

Location:
Berlin, Germany.
Joined:
Oct 21, 2023

🛠️ 📦 TypeScript Generics - a cheat sheet

Publish Date: Aug 15 '24
230 38

Generics in TypeScript can seem daunting at first - what’s with all those capital letters and square brackets? 😅

But don’t worry, this cheat sheet helps demystify Generics and show you how they work with simple examples.


How Generics Works

Generics enable you to define a type variable that serves as a placeholder for a specific type to be provided later. This is the core strength of Generics: they allow you to define flexible, reusable types without sacrificing type safety.

The letter T is commonly used by convention to represent a generic type variable, but you can use any letter or descriptive name that fits your context (other commonly used letters include U, V, K).

Usage examples

Below are some practical examples that demonstrate how Generics work.

You can play with the examples below in this TypeScript playground.

Generics Functions

// A generic function that works with any data type
function identity<T>(arg: T): T {
    return arg;
}

// Using the generic function with different types
let num = identity<number>(42); // T is number
let str = identity<string>("Hello"); // T is string

//TS automatically infers the type so this also works:
//let num = identity(42); // T is number
//let str = identity("Hello"); // T is string
Enter fullscreen mode Exit fullscreen mode

In this example, T is a type parameter that can represent any type. The actual type is determined when the function is called, whether it’s a number, string, or any other type.

Here, we've manually set the type to highlight how TypeScript handles different types with generics. However, it's important to remember that TypeScript typically infers the type automatically based on the argument provided. You don't need to manually specify the type unless you're overriding TypeScript's inference or dealing with complex types where explicit type annotations are necessary.

Generic Interfaces

Here, we define an interface Pair with two properties of different types. These types are specified when an instance is created and can be any type. The only constraint is that each property maintains its respective type.

interface Pair<T, K> {
  first: T;
  second: K;
}

let pair: Pair<string, number> = { first: "one", second: 2 }
let anotherPair: Pair<number, string> = { first: 1, second: "second" }
let otherPair: Pair<number, number> = { first: 1, second: 2 }
Enter fullscreen mode Exit fullscreen mode

Generics Types

Generics can also be applied to custom types. The process is similar to interfaces: you define a shape with placeholder types, which are determined when the type is used. Generics enforce typing but do not dictate specific types.

type Person<T, K, V> = {
  name: T,
  age: K,
  isMarried: V
}

const person1: Person<string, number, boolean> = {
  name: 'Bob',
  age: 67,
  isMarried: false
}
Enter fullscreen mode Exit fullscreen mode

Generic Classes

Generic classes allow you to define a blueprint where the data’s type is flexible, only being defined when an instance is created. In this example, the Box class can store content of any type (for example, a string, a number, or any other type).

class Box<T> {
  content: T

  constructor(content: T){
    this.content = content
  }

  getContent(): T {
    return this.content
  }
}

const letterBox = new Box('a')
const numberBox = new Box(1)
Enter fullscreen mode Exit fullscreen mode

Constraints

Generics also support constraints, allowing you to limit the types that can be used. For example, if you only want to support types with a length property, you can enforce this constraint using the extends keyword. It restricts the generic type variable to be a subtype of a particular type or interface. This ensures that the generic type must have certain properties or structure.

type LengthType = {length: number}
function getLength<T extends LengthType>(args: T){
  return args.length
}

getLength('abc')
getLength([1, 2])

//this doesn't work:
//getLength(2)
//getLength({"a": "b"})
Enter fullscreen mode Exit fullscreen mode

Utility types

Utility types in TypeScript use generics to create flexible and reusable type transformations. They simplify working with existing types by providing built-in operations.

interface User {
  name: string;
  age: number;
  location: string;
}

//makes all properties optional 
const partialUser: Partial<User> = {name: 'Alice'}

//allows to omit properties: here the name property is omitted
const omitUser: Omit<User, 'name'> = {location: 'Berlin', age: 33}
Enter fullscreen mode Exit fullscreen mode

I hope this was helpful!

Feel free to reach out if you have any questions! You can also find me on Github, LinkedIn, and Instagram.

Comments 38 total

  • Himanshu Sorathiya
    Himanshu Sorathiya Aug 15, 2024

    Yes, it's definitely useful for a person who's learning ts currently, thanks for this tips

  • Audrey Kadjar
    Audrey KadjarAug 15, 2024

    thanks @wizard798!!

  • Ender Akay
    Ender AkayAug 16, 2024

    Very helpful thanks!

  • João Angelo
    João AngeloAug 16, 2024

    Hi, Audrey Kadjar!
    Thanks for sharing
    Great article

  • Bhaskar Prajapati
    Bhaskar PrajapatiAug 16, 2024

    Definitely can be more refined. It is covering just the very beginner concept.

    • JasonOna
      JasonOnaAug 16, 2024

      How would you refine this? What would you like to include?

    • Audrey Kadjar
      Audrey KadjarAug 17, 2024

      @bh4skar I wanted to write a short article about generics to lay out the fundamentals. This article doesn't pretend to be exhaustive. I'm also curious: what would you like to include?

  • dev procedure
    dev procedureAug 16, 2024

    ARE GENERICS REALLY USEFUL in the REAL WORLD?

    Generics enable you to define a type variable that serves as a placeholder for a specific type to be provided later. This is the core strength of Generics: they allow you to define flexible, reusable types without sacrificing type safety.

    Let's think about the value of Generics. Are we really going to have a variable, a class, or a function where we have NO IDEA of the data that they should handle???

    When we initially give a variable a name, we should give it a GOOD NAME right off the start.

    But Generics would allow for VAGUE variable names as you say you don't really know what "type" the variable will handle, and we can decide later. Is that really a good thing? Not having any idea of what kind of data your variable is going to handle?

    If you don't even know what kind of data your variable is going to handle, can you even give that variable a GOOD DESCRIPTIVE NAME to begin with?

    Does Generics allow for "half-baked programming and design" to happen?

    • Levan Gharibashvili
      Levan GharibashviliAug 16, 2024

      You are not correct. There's lots of use cases for generics. For example collections: Array, Set, Map are all generic. They can work with any type and the semantics are same. You might have array of numbers, array of strings or array of certain objects. Without generics you would need to create separate Array classes for each type.
      Regarding naming, yes you can still give variables good names. For example, in Array.push method you would have an 'element' parameter. Even though it doesn't have a concrete type, the name is still descriptive.

    • Audrey Kadjar
      Audrey KadjarAug 17, 2024

      thanks for reading the article & your comment @devto2k5!

      I agree with @levan_gharibashvili_7fcac. There are a lot of use cases for generics: they are powerful because they allow flexibility without sacrificing type safety.

      Generics are not about having "no idea" of the data being handled; they are about designing systems that can handle a variety of types in a structured, predictable manner.

      • dev procedure
        dev procedureAug 17, 2024

        The responses to defend generics and saying there are use cases only refer BACK to built-in functions, that is,

        most array methods, set methods, map methods, etc.

        But that is the Framework, Library World of making more functions, variables.
        And where you have NO IDEA of what the variable name or function will be.
        That is, The Framework and Library World.

        But that is NOT the REAL world of PHYSICAL products or services, like stuff you buy at Home Depot or Walmart.

        NOTE: We just had 3 responses (to my comment) defending generics, but none actually gave a REAL world examples.

        • Steve Schafer
          Steve SchaferAug 19, 2024

          But that is NOT the REAL world of PHYSICAL products or services, like stuff you buy at Home Depot or Walmart.

          Sure it is. When I go to Home Depot to buy some screws, I go to the screws section, which contains all manner of screws. Different screws have different properties, but many of them share values of those properties. For example, Screw<Wood> has a certain shape, which is very different from the shape of Screw<Machine>. The point of generics here is that sometimes I need to know whether a screw is a Screw<Wood> vs. a Screw<Machine> (e.g., thread specifications for wood screws and machine screws are different), and other times I just need to know that it is a Screw<T>, where T remains unspecified (e.g., screw material, length, and quantity per package are not dependent on whether a screw is a wood screw or a machine screw).

          When I'm buying screws at Home Depot, I'm not literally classifying them into generic types the way I would if I were writing a software application, but I am effectively doing that as I search for the screws that I need.

          • dev procedure
            dev procedureAug 19, 2024

            The point of generics here is that sometimes I need to know whether a screw is a Screw vs. a Screw (e.g., thread specifications for wood screws and machine screws are different),

            But since you already know that Wood Screws are different than Machine Screws AND you know the properties of each type of screw, WHY would I need to use generics?

            Couldn't you just describe each screw's properties in the DATABASE (as you would have to anyway?) and use a few look-up tables for each screw type?

            And if I wanted to add a new screw type, why wouldn't I just take an existing screw type, and modify that one? Via Copy and Paste?

            Also, any major app is going to have to save to a database anyway, so are you going to save "vagueness" to the database? Isn't it just real stuff, important stuff that is worth saving to a database?

            Using Generics for screws seems like a waste of time and adds a layer of complexity that has essentially zero benefits, short-term or even long-term.

            That is, I would NOT need to decipher stuff like
            and hence I would not need a "cheat sheet" as I could read the code far more easily.

            • Steve Schafer
              Steve SchaferAug 20, 2024

              You're completely missing the point. Generics aren't about what's different; they're about what's the same. If I want to sort an array of screws by length, having separate WoodScrew.SortByLength() and MachineScrew.SortByLength() methods is just duplicating code. Instead, I define an ISortableByLength interface, and the vast majority of different kinds of screws can use a single implementation that only cares about length.

    • Ben Duncan
      Ben DuncanAug 17, 2024

      Generics have nothing to do with naming, and are more about writing functions that work on different argument types and return a properly constrained type based on the input.

      Many utility functions work for different types. Nearly all built-in utility functions are generics: most array methods, set methods, map methods, etc.

      Imagine you want to make a new utility that takes a function with a callback and converts it to async/promise function, and returns the correct type at the end. This is trivial with generics and impossible without.

    • Atul Anand Oraon
      Atul Anand OraonAug 20, 2024

      Interesting perspective.

      I won't support nor oppose your suggestions.
      I too had similar thoughts starting with Typescript.

      But I needed to use it.
      Especially on the frontend for the React as the functions expect the elements to be passed like,
      HTMLElement<Something>

      It's something we can't escape.

  • JWP
    JWPAug 16, 2024

    Yes generics, first implemented in C# opened the door for using common methods to process lists of any type. A list of Person would also work on a list of an item, to sort, to order, to reverse, to allow functional support for anything.

    Unlike Javascript arrays, type safety in Generics prevented unintended consequences.

    In the end it's all a contract of behaviors applied liberally across types.

    Why experience runtime failures when not needed?The compiler becomes a best friend to programmers with no need for linting.

  • Ben Duncan
    Ben DuncanAug 17, 2024

    You don't actually need to call the identity function with a type like in your example.

    It will infer the type from the argument, so unless you need to override an incorrect inference you can just call it like normal:

    // A generic function that works with any data type
    function identity<T>(arg: T): T {
        return arg;
    }
    
    // Using the generic function with different types
    let num = identity(42); // T is number
    let str = identity("Hello"); // T is string
    
    Enter fullscreen mode Exit fullscreen mode
    • Audrey Kadjar
      Audrey KadjarAug 17, 2024

      thanks for your comment @bendman :)

      yes, you are correct. I am aware of this, but in my examples, I specified the types because I wanted to highlight how TypeScript handles different types with generics. I've added a note about this in my article to clarify this.

  • Ofek Shilon
    Ofek ShilonAug 17, 2024

    Without further constraints, Pair can be instantiated with identical types for both arguments.

  • Trang Tung Nguyen
    Trang Tung NguyenAug 17, 2024

    Great article! Thanks for sharing!

  • Alexander Opalic
    Alexander OpalicAug 18, 2024

    really nice blog post that shows the core concepts of generics

  • Amir 1998
    Amir 1998Aug 18, 2024

    🙏❤️🤩✅😎❤️

  • xcfio
    xcfioAug 19, 2024

    You forgot to add example of implicit generic.

  • Atul Anand Oraon
    Atul Anand OraonAug 20, 2024

    This is really awesome 👍.
    Lots of learning

  • Santiago Arizti
    Santiago AriztiAug 21, 2024

    Nice article, but this is not a cheat sheet, more like a tutorial.

  • Kevin Ready
    Kevin ReadyAug 22, 2024

    Thank you for demystifying a basic ingredient in TypeScript succinctly. Great article!

  • Lucas Lachance
    Lucas LachanceDec 9, 2024

    If Typescript developers could please stop writing Generics as if they were writing pre-minified code, that would be great.

    What's the problem with defining K as Key , V as Value , and T as CustomType or something? Are you all afraid of writing longer names? Do you name all your variables a, b, c, d ?

    • Lucas Lachance
      Lucas LachanceDec 9, 2024
      interface U {
        n: string;
        a: number;
        l: string;
      }
      
      //makes all properties optional 
      const pU: Partial<U> = {n: 'Alice'}
      
      //allows to omit properties: here the name property is omitted
      const oU: Omit<U, 'n'> = {l: 'Berlin', a: 33}
      
      Enter fullscreen mode Exit fullscreen mode

      This is how you're writing generics, guys. Stop it.

Add comment