# The secret sauce of generics: inference

In our last lesson, we covered the differences between generic functions and generic types. We also left a mystery unsolved - why can we call generic functions without passing type arguments?

## Linking Runtime Arguments To Type Arguments

In our previous investigations of generic functions, I've been a bit devious. I've been showing you one very specific type of generic function - one that receives no runtime arguments.

```ts twoslash
// Receives 1 type argument, and no runtime arguments
function createSet<T>(): Set<T> {
  return new Set();
}
```

What would happen if we wanted to let users pass a runtime argument to our generic function? We'd first need to declare a parameter of `initialValues`, then pass that to the `Set` constructor:

```ts twoslash
// @errors: 7006
function createSet<T>(initialValues): Set<T> {
  return new Set(initialValues);
}
```

But `initialValues` is currently untyped. How would we type it? We could use `any[]`:

```ts twoslash
function createSet<T>(initialValues: any[]): Set<T> {
  return new Set(initialValues);
}
```

But now, any type can be passed to `createSet`:

```ts twoslash
function createSet<T>(initialValues: any[]): Set<T> {
  return new Set(initialValues);
}

// ---cut---
const setOfStrings = createSet<string>([1, 2, 3]); // No error!
```

Here, we're creating a set of strings, but passing an array of numbers - and TypeScript doesn't complain.

We actually want to link our runtime arguments to the type provided in the type argument. We can do that by typing `initialValues` as `T[]`:

```ts twoslash
function createSet<T>(initialValues: T[]): Set<T> {
  return new Set(initialValues);
}
```

We've linked our runtime parameter, `initialValues`, to our type parameter, `T`. In the vast majority of generic functions, you'll see this pattern - a runtime argument linked to a type argument. This means that whatever gets passed in to `initialValues` MUST correspond to the type of `T` - or, in this case, be an array of `T`.

This was my little trick - none of the examples we've seen so far have required this link. So, let's test it out a bit.

## Type Arguments And Generic Functions

There are now a few different ways to call our `createSet` function. We can call it by passing a type argument. If we do, `T` will be whatever type argument we pass it:

```ts twoslash
function createSet<T>(initialValues: T[]): Set<T> {
  return new Set(initialValues);
}

// ---cut---
const setOfStrings = createSet<string>(["a", "b", "c"]);
```

If we pass a runtime argument that doesn't match the type argument, TypeScript will show an error:

```ts twoslash
// @errors: 2322
function createSet<T>(initialValues: T[]): Set<T> {
  return new Set(initialValues);
}

// ---cut---
const setOfStrings = createSet<string>(["a", "b", 1]);
```

But what happens if we don't pass in a type argument? Drumroll, please...

```ts twoslash
function createSet<T>(initialValues: T[]): Set<T> {
  return new Set(initialValues);
}

// ---cut---
const setOfStrings = createSet(["a", "b", "c"]);
//    ^?
```

That's right - `setOfStrings` is a `Set<string>`. TypeScript has inferred the type of `T` from our runtime argument.

Let me say that again - TypeScript inferred the type of `T` based on the runtime argument we passed in. It's magic.

## The Power Of Inference

This is the reason why generic functions are so powerful in TypeScript. A user of `createSet` doesn't even need to think about type annotations. They can just pass in an array of strings, and TypeScript will infer the returned `Set` for them.

Let's hammer this home with another example, that also uses `Set`: `uniqueArray`.

```ts twoslash
function uniqueArray(array: any[]) {
  return Array.from(new Set(array));
}
```

This function takes in an array, creates a `Set` from it, and then converts it back to an array. It's a simple way to remove duplicates from an array.

But it currently takes in `any[]`. This means that whatever we pass in, we'll get `any[]` back:

```ts twoslash
function uniqueArray(array: any[]) {
  return Array.from(new Set(array));
}

// ---cut---
const uniqueStrings = uniqueArray(["a", "b", "a"]);
//    ^?
```

By adding a type parameter, we can get `uniqueArray` to infer the type it's passed, and return that type:

```ts twoslash
function uniqueArray<T>(array: T[]): T[] {
  return Array.from(new Set(array));
}
```

We refer to `T` in the runtime argument, `array`, and mark the return type as `T[]`.

Now, `uniqueStrings` is a `string[]`:

```ts twoslash
function uniqueArray<T>(array: T[]): T[] {
  return Array.from(new Set(array));
}

// ---cut---
const uniqueStrings = uniqueArray(["a", "b", "a"]);
//    ^?
```

Not only has this made our function easier to use, but we've removed a potential source of 'any' in our codebase. We've made our code safer, smarter AND easier to use.

## Summary

- You don't have to pass type arguments to generic functions
- If you do pass a type argument, it becomes the source of truth for the function
- If you don't pass a type argument, TypeScript will attempt to infer it from the runtime arguments
- You can link runtime arguments to type arguments by referring to the type arguments (`T`) in the runtime arguments (`initialValues`)
- Users of your generic functions can pass in runtime arguments without worrying about type annotations

In our next lesson, we'll be learning how to use our new-found powers. We'll learn _when_ you should make a function generic - and what kinds of problems generic functions can solve.

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