# Turning types into type functions 🤯

In our first lesson, we talked about the main thing people feel confused about when it comes to generics: the term 'generic' itself.

We're going to start our journey by looking at generic types. I think they're the easiest to pick up and start using.

You'll learn how generic types can reduce repetition in your types, just like functions reduce repetition in your runtime code. And if you've never used them before, you'll soon wonder how you ever lived without them.

## The Problem Generic Types Solve

```ts twoslash
type User = {
  id: string;
  firstName: string | null | undefined;
  lastName: string | null | undefined;
  phone: string | null | undefined;
};
```

```ts twoslash
type Maybe = null | undefined;

type User = {
  id: string;
  firstName: string | Maybe;
  lastName: string | Maybe;
  phone: string | Maybe;
};
```

But this feels a bit strange. `firstName` isn't 'string or maybe', it's 'maybe a string'. 'Maybe' isn't separate from `string`, it's a descriptor of `string`.

The right API would feel something like this:

```ts twoslash
type Maybe<T> = T | null | undefined;

// ---cut---
type User = {
  id: string;
  firstName: Maybe<string>;
  lastName: Maybe<string>;
  phone: Maybe<string>;
};
```

Now, we're applying `Maybe` to `string`. It's as if `Maybe` is a function, and we're passing an argument to it.

But how do we make this API work?

## Our First Generic Type

We can turn `Maybe` into a generic type by adding a type parameter:

```ts twoslash
type Maybe<T> = null | undefined;
//         ^ Type Parameter
```

We place it just after the type's name, inside angle brackets. We can call it whatever we want, but `T` is a common choice.

We've turned our `Maybe` type from a _variable_ into a _function_. We can think of everything after the `=` as what our function returns.

Currently, we're not using `T` in the returned type. Whatever we pass in to `Maybe`, we'll always get back `null | undefined`.

Let's game this out for a second. If we pass in `string` to `Maybe`, we should get back `string | null | undefined`.

```ts twoslash
type Maybe<T> = T | null | undefined;
// ---cut---
type MaybeString = Maybe<string>; // string | null | undefined
```

So whatever we pass in needs to be used in a union with `null | undefined`. We can do this by adding `T` to the returned type:

```ts twoslash
type Maybe<T> = T | null | undefined;
```

Now, whatever we put into `Maybe`, we get back that type or `null` or `undefined`:

```ts twoslash
type Maybe<T> = T | null | undefined;

// ---cut---
type MaybeString = Maybe<string>; // string | null | undefined
type MaybeNumber = Maybe<number>; // number | null | undefined
type MaybeBoolean = Maybe<boolean>; // boolean | null | undefined
```

This is great, because we've saved ourselves a lot of keystrokes - and we've also got a single source of truth for our `Maybe` type.

## Anatomy Of A Generic Type

Let's break down the anatomy of a generic type, and how it differs from a normal type.

```ts
// Generic Type
type Maybe<T> = T | null | undefined;

// Type
type Maybe = null | undefined;
```

Syntactically, they really are extremely similar. The only difference is the type parameter `T` in the generic type.

But as a mental model, they're quite different. The difference is like the difference between a function and a variable:

```ts
// Function
const add = (a, b) => {
  return a + b;
};

// Variable
const add = 5;
```

The variable is static - it's always 5. The function is dynamic - it can return different values based on its arguments.

And in order to produce the returned value, you have to _call_ the function with an argument.

In the same way, to produce a type from a generic type, you have to _call_ it with a type argument.

Even though it's not a runtime function, I like to use the term 'call'. We're 'calling' `Maybe` with `string` to get `string | null | undefined`. If you wanted to be more precise, you could say we're 'instantiating' `Maybe`.

So, a generic type is just a normal type alias with a type parameter. It's like a function for types.

### Can `interface` Be Generic?

`interface` can be generic too. The syntax is the same as for a type alias:

```ts twoslash
interface Box<T> {
  value: T;
}
```

The same rules apply for `interface` as for a type alias. They can't represent unions (so we couldn't use `interface` to define a `Maybe` type) but they can be generic.

## One More Example

Let's firm up what we've learned so far by looking at another real-world example.

Consider these `StreamingPlaylist` and `StreamingAlbum` types:

```ts twoslash
type StreamingPlaylist =
  | {
      status: "available";
      content: {
        id: number;
        name: string;
        tracks: string[];
      };
    }
  | {
      status: "unavailable";
      reason: string;
    };

type StreamingAlbum =
  | {
      status: "available";
      content: {
        id: number;
        title: string;
        artist: string;
        tracks: string[];
      };
    }
  | {
      status: "unavailable";
      reason: string;
    };
```

Both of these types share similar structures. They represent a streaming resource that is either available with specific content or unavailable - with a reason for its unavailability.

The difference lies in the structure of the `content` object: the `StreamingPlaylist` type has a `name` property, while the `StreamingAlbum` type has a `title` and `artist` property.

When you see duplicated code like this, it's a good sign that you can use a generic type to reduce repetition.

Since the only difference between these types is the `content` object, we can create a generic type that represents a streaming resource:

```ts twoslash
type StreamingResource<TContent> =
  | {
      status: "available";
      content: TContent;
    }
  | {
      status: "unavailable";
      reason: string;
    };
```

Now, we can use this generic type to define both `StreamingPlaylist` and `StreamingAlbum`:

```ts twoslash
type StreamingResource<TContent> =
  | {
      status: "available";
      content: TContent;
    }
  | {
      status: "unavailable";
      reason: string;
    };

// ---cut---
type StreamingPlaylist = StreamingResource<{
  id: number;
  name: string;
  tracks: string[];
}>;

type StreamingAlbum = StreamingResource<{
  id: number;
  title: string;
  artist: string;
  tracks: string[];
}>;
```

We've reduced the duplication of our code, created a pattern that's easy to follow, and made our code easier to read and understand.

## Summary

- Generic types are like functions for types
- Just like functions, you have to call them with a type argument to get a type back
- The only difference between a generic type and a normal type is that generic types have a type parameter
- `interface` can be generic too
- Use generic types when you want to reduce repetition in your types

In the next lesson, we'll dive deeper into type parameters. We'll learn how to use constraints and defaults to build even more flexible and powerful generic types.

Want to get the next lesson now? Click the link below.
