# ARTICLE: The “Hello World” of generic functions

In our last lesson, we took our first look at generic functions. We saw that a generic function can receive a type argument, just like a generic type.

In this lesson, we're going to build our first generic function - one that accepts a type argument.

## Why Are Type Arguments Useful?

We know that a generic function can receive a type argument. But why would we want to do that?

Let's look at a simple example: a function that creates a `Set`.

```ts twoslash
const createSet = () => {
  return new Set();
};
```

We could imagine that this function has some reason to exist. Perhaps it adds some extra functionality to the `Set` creation process. Maybe it logs whenever you add an member, or sets up some default values.

We know from our previous exercise that without a type argument (and without a runtime argument), TypeScript doesn't know what type `Set` is supposed to be.

So, creating our `Set` from within a function means we lose the ability to specify the type of the `Set`.

```ts twoslash
const createSet = () => {
  return new Set();
};
// ---cut---
const stringSet = createSet();

stringSet.add("Hello");
stringSet.add(123); // Should be an error!
```

So, we can see our function needs type information in order to be useful. We need a type parameter.

## Specifying Type Parameters

Our ideal API would let us pass a type argument to `createSet` to specify the type of the `Set`.

```ts twoslash
// @errors: 2558
const createSet = () => {
  return new Set();
};
// ---cut---
const stringSet = createSet<string>();
```

But we're getting an error: 'Expected 0 type arguments, but got 1.'

So, we need to declare a type parameter on `createSet`:

```ts twoslash
const createSet = <T>() => {
  return new Set();
};
```

Now, we can pass a type argument to `createSet`:

```ts twoslash
const createSet = <T>() => {
  return new Set();
};

// ---cut---
const stringSet = createSet<string>();
//    ^?
```

But there's an issue - our `Set` is still not typed. It's still a `Set<unknown>`. Why is this?

## Using Type Parameters

Well, we've added a type parameter to `createSet`, but we're not using it!

```ts twoslash
const createSet = <T>() => {
  // Not using T here!
  return new Set();
};
```

In an IDE, we can see this by hovering over `T`. It'll show a message saying "`T` is declared but its value is never read".

The `Set` we're returning is still un-typed. We need to somehow relate it to the `T` in our type parameter.

There are several ways to do this. We could pass it to `Set` as a type argument:

```ts twoslash
const createSet = <T>() => {
  return new Set<T>();
};
```

We could declare a type for the `Set`:

```ts twoslash
const createSet = <T>() => {
  const set: Set<T> = new Set();
  return set;
};
```

Or, we could use a return type on `createSet`:

```ts twoslash
const createSet = <T>(): Set<T> => {
  return new Set();
};
```

Either approach is fine - it's up to you which you prefer. I like the simplicity of passing the type argument directly to `Set`.

Now, our `createSet` function can accept a type argument, and that type argument will be used to type the `Set` it creates.

```ts twoslash
const createSet = <T>(): Set<T> => {
  return new Set();
};

// ---cut---
const stringSet = createSet<string>();
//    ^?
```

## When Should You Use Type Arguments?

This is the first step towards designing good generic functions - understanding that type arguments can be just as important as runtime arguments. But when should you use them?

A rule of thumb I like to use is this: does the return type of the function depend on what's passed in?

In the case of our `createSet`, the return type _does_ depend on the type argument. If we pass in `string`, we need a `Set<string>`. If we pass in `number`, we need a `Set<number>`.

A `createMap` function might be similar. If we pass in `string` and `number`, we need a `Map<string, number>`:

```ts twoslash
const createMap = <K, V>() => {
  return new Map<K, V>();
};

const map = createMap<string, number>();
```

Just like in our `createSet` function, the return type of `createMap` depends on the type arguments passed in.

## Summary

- You can specify type parameters on a generic function using angle brackets (`<` and `>`) before the function's arguments.
- The type parameter must be _used_ in the function to have any effect.
- If the return type of the function depends on the type argument, you should use a type parameter.

In our next lesson, we'll be looking at the differences between generic types and generic functions - and at what happens when we _don't_ pass in a type argument to `createSet`.

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