# Generic Functions vs Generic Types

In our last lesson, we built our first generic function - one that was designed to receive a type argument.

Generic functions and generic types share a lot of similarities - but they also have some key differences. Let's take a look at them.

## Generic Functions Can Be Constrained

In our previous exercises, we saw that generic types can be constrained.

We used a constraint on our `Result` type to ensure that the `TError` type had a `message` property.

```ts twoslash
type Result<T, TError extends { message: string }> =
  | { success: true; value: T }
  | { success: false; error: TError };
```

The `extends` keyword is used to add a constraint to a generic type.

We can do the same with our `createSet` function. Let's imagine that we only want to allow an object with an `{id: string}` to be passed in:

```ts twoslash
function createSet<T extends { id: string }>(): Set<T> {
  return new Set();
}
```

Now, our `createSet` function can only be passed a type argument that has an `id` property:

```ts twoslash
function createSet<T extends { id: string }>(): Set<T> {
  return new Set();
}

// ---cut---
const setOfUsers = createSet<{
  id: string;
  name: string;
}>();

setOfUsers.add({ id: "1", name: "Alice" });
```

These constraints behave exactly the same way as in generic types.

## Generic Functions Can Have Defaults

Just like generic types, generic functions can have default type arguments.

In generic types, we set defaults by using the `=` operator:

```ts twoslash
type Result<T, TError = { message: string }> =
  | { success: true; value: T }
  | { success: false; error: TError };
```

This would mean that using `Result<string>` would default `TError` to `{ message: string }`.

We can do the same with our `createSet` function. Let's default the type argument to `string`:

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

Now, if we call `createSet` without a type argument, it will default to `string`:

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

// ---cut---
const setOfStrings = createSet();

setOfStrings.add("Hello, world!");
setOfStrings.add(123);
```

This makes things pretty simple to learn: generic functions can be constrained and defaulted in the same way as generic types.

But there is one significant difference between them:

## Generic Functions Don't Require Type Arguments

When you 'call' a generic type, you must pass all the type arguments that don't have defaults:

```ts twoslash
// @errors: 2707

type Result<T, TError = { message: string }> =
  | { success: true; value: T }
  | { success: false; error: TError };

type Example1 = Result<string>;

type Example2 = Result;
```

In this example, `Result` is missing a type argument, so TypeScript will throw an error.

But with generic functions, you _don't need to pass the type arguments_. If we look at our `createSet` function, we can see that we can call it without any type arguments:

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

// ---cut---
const setOfSomething = createSet(); // No error!
```

If we think about generic types, this seems extremely strange. Why would TypeScript allow us to call a generic function without passing any type arguments?

We'll explore this particular question in the next lesson. But for now, there's another question that comes to mind. What type will `setOfSomething` be?

### The Hidden Defaults of Generic Functions

When you call a generic function without passing any type arguments, TypeScript will default the type arguments in the following order:

If the type argument has a default, TypeScript will use that default.

If we add a default to `createSet`, TypeScript will use that default:

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

const setOfStrings = createSet();
//    ^?
```

If the type argument has a constraint, TypeScript will use that constraint as the default:

```ts twoslash
function createSet<T extends { id: string }>(): Set<T> {
  return new Set();
}

const setOfThingsWithId = createSet();
//    ^?
```

If the type argument has no default or constraint, TypeScript will default it to `unknown`:

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

const setOfUnknowns = createSet();
//    ^?
```

## Can You Enforce Passing Type Arguments To Generic Functions?

There's no built-in way for TypeScript to enforce passing type arguments to generic functions. There is a slightly janky way that the community has found to make it work in some niche cases.

If our type argument is unconstrained, we can use a slightly wonky-looking default:

```ts twoslash
// @errors: 2345
function createSet<
  T = "You must pass a type argument to createSet",
>(): Set<T> {
  return new Set();
}

const set = createSet();

set.add("Hello, world!");
```

Now, our `createSet` function will default to a string that tells the user they must pass a type argument. This shows up nicely in the errors.

Of course, this only works if our type argument is unconstrained. If we try to constrain it to something that doesn't match our default, TypeScript will throw an error:

```ts twoslash
// @errors: 2344
function createSet<
  T extends {
    id: string;
  } = "You must pass a type argument to createSet",
>(): Set<T> {
  return new Set();
}
```

So this is a bit of a hack, but it can be useful in some cases.

## Summary

- Generic functions can be constrained and defaulted in the same way as generic types.
- Generic functions don't require type arguments.
- If you don't pass a type argument to a generic function, TypeScript will first look for a default, then a constraint, and finally default to `unknown`.
- There's no built-in way to enforce passing type arguments to generic functions, but you can use a default string to help with this.

In our next lesson, we'll be learning why TypeScript lets us call generic functions without passing type arguments. It's one of the most powerful features of TypeScript's type system.

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