# How do you know when a function should be generic?

In our last lesson, we discovered the secret sauce of generic functions: inference. In this lesson, we'll widen this out to investigate when you should make a function generic.

## Functions Interrupt TypeScript's Inference

TypeScript is a marvel - it can look at your code and understand its logical flow. Take the following example:

```ts twoslash
const isProblemOrSolution = (filename: string) => {
  const splitFilename = filename.split(".");

  const finalIndex = splitFilename.length - 1;

  const extension = splitFilename[finalIndex];

  const isProblem = extension === "problem";

  const isSolution = extension === "solution";

  return isProblem || isSolution;
};
```

We've only provided one type annotation - `filename` is a `string`. But without any other annotations, TypeScript knows that:

- `splitFilename` is an array of strings
- `finalIndex` is a number
- `extension` is a string or `undefined`
- `isProblem` and `isSolution` are booleans

And from this, it understands that `isProblemOrSolution` returns a boolean.

But what about if we extract the logic for `isProblem` and `isSolution` into separate functions?

```ts twoslash
// @errors: 7006
const isProblem = (extension) => extension === "problem";

const isSolution = (extension) => extension === "solution";
```

TypeScript can't infer the type of `extension` in these functions. We need to provide a type annotation:

```ts twoslash
const isProblem = (extension: string) =>
  extension === "problem";

const isSolution = (extension: string) =>
  extension === "solution";
```

Whatever you do, you MUST always annotate the parameters a function takes. So, a function is a kind of interruption in TypeScript's flow of inference.

### An Example of Interrupting Inference

Let's take our `uniqueArray` function from the last lesson:

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

This function is an interruption to TypeScript's inference. It takes in an `any[]`, so TypeScript can't infer the type it returns:

```ts twoslash
const uniqueArray = (array: any[]) => {
  return Array.from(new Set(array));
};
// ---cut---
const processTags = (tags: string[]) => {
  const uniqueTags = uniqueArray(tags);
  //    ^?

  return uniqueTags;
};
```

In fact, we'd be better off just inlining the logic from `uniqueArray` into `processTags`:

```ts twoslash
const processTags = (tags: string[]) => {
  const uniqueTags = Array.from(new Set(tags));
  //    ^?

  return uniqueTags;
};
```

Now, the inference is back - because TypeScript's inference flow hasn't been interrupted by the `any` type.

## Type Dependencies

So, when should you make a function generic? When you want to preserve TypeScript's inference flow.

Our `uniqueArray` function, once generic, keeps TypeScript's inference intact:

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

const processTags = (tags: string[]) => {
  const uniqueTags = uniqueArray(tags);
  //    ^?

  return uniqueTags;
};
```

To put this more technically, the return type of `uniqueArray` depends on the type of what gets passed in. I'll call this a type dependency.

**If you detect a type dependency in your function, you should make it generic**.

Let's imagine another function like `toString`:

```ts twoslash
const toString = (value: unknown) => {
  return String(value);
};
```

`toString` has no type dependencies. Its return type (`string`) is always the same, regardless of what you pass in. So, it doesn't need to be generic.

### Type Dependencies In Parameters

There's another kind of type dependency we should mention. `uniqueArray` has a return type that depends on its parameter. But some functions have parameters that depend on each other.

Imagine a `toIds` function that takes an array of objects and returns an array of their IDs:

```ts twoslash
const toIds = <T>(
  arr: T[],
  mapper: (member: T) => string,
): string[] => {
  return arr.map(mapper);
};

const ids = toIds(
  [{ itemId: "1" }, { itemId: "2" }],
  (member) => member.itemId,
);
```

Let's break this down.

`toIds` always returns the same type - an array of strings. It takes in two parameters:

- `arr`, an array of something
- `mapper`, a function that takes a member of `arr` and returns a string

Here, there's a type dependency between two _parameters_. The type of the `mapper` function depends on the type of `arr`.

So, the function needs to be generic - even though the return type is always the same.

## Summary

- Functions interrupt TypeScript's inference flow.
- Sometimes, those functions make TypeScript lose track of types.
- A type dependency is when one type in your function depends on another.
- A common type dependency is when a function's return type depends on its parameter.
- One parameter can also depend on the type of another.
- If you detect a type dependency, make your function generic.

In our next lesson, we'll take a look at generic components in React. They're a beautiful way of making your components more flexible and reusable.

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