TypeScript Generics
Tags: typescript, generics
Published: 2021-03-21 22:09:54

---
canonical_url: "https://ajcwebdev.com/typescript-generics/"
description: "Generics are a way to prepare types for the unknown."
---

## Outline

- [Introduction](#introduction)
  - [Generics Dictionary Definition](#generics-dictionary-definition)
  - [A Generic is Like a Function](#a-generic-is-like-a-function)
  - [Generics Enable Reusable Types](#generics-enable-reusable-types)
  - [Generic Constraints](#generic-constraints)

## Introduction

I have a lot of opinions about TypeScript, which is probably bad because I don't actually know anything about TypeScript.

In an attempt to lessen my ignorance I shouted into the void that is the [Lunch Dev Discord](https://discord.com/invite/dx7ZWCy) for an explanation of a particularly slippery concept, [generics](https://www.typescriptlang.org/docs/handbook/2/generics.html). I've had generics explained to me multiple times for multiple languages including Java, C#, and TypeScript but it never sticks.

### Generics Dictionary Definition

Wiki's surely-correct-but-not-very-useful definition:

> _Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters_

I then remembered that [Chan](https://twitter.com/chantastic) told me to [buy a book](https://www.smashingmagazine.com/printed-books/typescript-in-50-lessons/) for this exact reason. According to TypeScript in 50 Lessons:

> _I don't know what I want, but I know how to get it._

Very zen.

> _Generics are a way to prepare types for the unknown. Whenever we know a certain behavior of a type but can't exactly say which type it affects, a generic helps us to model this behavior._

When I read that I think:

_I know what I want, but I don't know what the thing I want is actually going to do, so do I really know what I want?_

### A Generic is Like a Function

Enter [Alex Anderson](https://twitter.com/ralex1993), professional TypeScript explainer.

> __In TypeScript at least (can't speak for other language nuance), a Generic is like a function - you put a type in, and it transforms it into a different type.__
>
> __Suppose you have a function:__

```typescript
function sum(numA, numB) {
  return numA + numB;
}
```

> __You know that you are going to pass it two values, but those two values could be anything. They could be `1`, `0`, `-1` or `Infinity`, but you don't actually know what the result is going to be until you call it.__

Okay that makes sense at least, but what's the use case?

### Generics Enable Reusable Types

> __Just like functions, the use case is making more reusable types. If you wanted to, you could inline every single function call and your program would still work. But that would be a pain to write. Same thing with Generics.__
>
> __Let me grab an example from my [TypeScript Course™](https://ui.dev/typescript/). Very simple, almost contrived example:__

```typescript
function getFirstNumberItem(list: number[]): number {
  return list[0];
}
```

> __Now rewrite that for strings:__

```typescript
function getFirstStringItem(list: string[]): string {
  return list[0];
}
```

> __Notice that the function implementation is *exactly* the same, but we have to rewrite the entire function because the types are different. Stupid TypeScript ruining our day!😠__

Okay, so I'm getting kind of an `any` vibe but for functions instead of types, is that in the right direction?

> __That's part of it; `any` is not type safe; You might create runtime type errors if you use `any`.__
>
> __With Generics, you either pass it a type as an argument, to say "I would like to get the first item out this array, oh, and by the way, the array holds strings", or TypeScript infers what the generic type argument is from its usage.__
>
> __Which is what happens with this generic function:__

```typescript
function getFirstItem<T>(list: T[]): T {
  return list[0];
}
```

```typescript
const stringItem = getFirstItem(["a","b","c"])
// TypeScript knows stringItem is a string
```

> __However, you can use generics with functions, interfaces, type aliases, and classes. Let's look at another example from my [course™](https://ui.dev/typescript/).__
>
> __Here's a type which represents a tree of strings. It allows you to create an infinitely nested set of objects with `left` and `right` properties, where each node has a string value.__

```typescript
type StringTree = {
  value: string;
  left?: StringTree;
  right?: StringTree;
}
```

> __But what if you wanted a tree of numbers? Or a tree of more complicated objects? Either you create a new type definition for each (`NumberTree`, `FruitTree`, etc) or you create a generic tree type.__

```typescript
type Tree<T> = {
  value: T;
  left?: Tree<T>;
  right?: Tree<T>;
}
```

> __And then we can `recreate` our StringTree type by explicitly passing the `Tree` generic a `String` type.__

### Generic Constraints

A wild [Ben](https://twitter.com/bendmyers) appears!

>> __So is the generic a way to say "It can be any type, so long as they're all consistently the same type"?__

> __Yes. Anywhere I use the type parameter (`T` or whatever people use) in my type definition represents the same type. One more example from my [course™](https://ui.dev/typescript/).__

```typescript
class FruitBasket<T extends Fruit> {
  constructor(public fruits: T[] = []) {}
  add(fruit: T) {
    this.fruits.push(fruit);
  }
  eat() {
    this.fruits.pop();
  }
}
```

> __We use `T` in two places:__
> 
> __- For the type of the array items in the `fruits` property.__
> __- As the type of thing that we pass to the `add` method, which adds a specific fruit to our basket.__
> 
> __This class has a generic constraint. That's the part at the top where we say `<T extends Fruit>`. `Fruit` is a class, which means we can only use instances of that class with this `FruitBasket` class.__
> 
> __This lets us create `FruitBasket` instances for `Apple` and `Banana` classes that extend from `Fruit`, but we can't create an `Onion` or `Lettuce` `FruitBasket` instance, because they don't extend from `Fruit`.__
>
> __Re generic constraints:__

```typescript
class Fruit {
  isFruit:true;
  constructor(public name:string) {}
}
class Apple extends Fruit {
  type:"Apple",
  constructor() {
    super("Apple")
  }
}
class Vegetable {
  isFruit: false // This makes Vegetable incompatible with Fruit
  constructor(public name: string) {}
}

const appleBasket = new FruitBasket<Apple>(); // This works

const vegetableBasket = new FruitBasket<Vegetable>();
// Type Error: Type 'Vegetable' does not satisfy the constraint 'Fruit'. Types of property 'isFruit' are incompatible.
```

If this Socratic Dialogue was useful, check out [Socrates's course](https://ui.dev/typescript/).