How to Use Generics in TypeScript

Generics in TypeScript is a way to make your code more abstract and reusable.

Let's say you want to create a function that returns a first element from a given list:

How to type it? We cannot know beforehand list of what type will be given.

We could use overloads to enumerate every possible type we accept:

...Or we could use a union type:

...But all of those ways have a common issue. If we want to pass, for example, an object, we have to define it in the type manually. This isn't convenient, and TypeScript has a better way to do it.

Generics#

Generic type is a type that accepts arguments and uses them in some way. In the case of our head function, we can declare:

The angle brackets define a type-parameter. This parameter is used by TypeScript compiler to define the TEntity type later. So, when we use this function like this:

...the TEntity becomes a number.

When we use the function like this:

...the TEntity becomes a string.

We don't have to manually define a type now, since TypeScript will automatically defer it from the argument.

It is easier to think about generics as of “type-functions”. They accept some type-parameters and produce a result-type.

It will even defer a union type for you if pass an array of elements with different types.

Generic Types and Interfaces#

With generics, you can describe custom types and interfaces in more abstract way.

Let's say you want to create a type-alias for array and call it List. You would use this type somehow like this:

With TypeScript, you can define a generic type for this:

You can read it as a ”type-function“. It takes an argument TEntity and returns an array of TEntity.

Same with interfaces. You can define an interface with type-parameters to set a type which this interface can work with:

Most of the time generic types and interfaces are used to define type-constraints.

Generic Constraints#

You can also set constraints for your generic types to make sure it is used properly.

Let's say you want your EventHandler to be used only with MouseEvent or KeyboardEvent. Then, you can specify those types as a constraint:

You can even define a constraint based on a type-parameter!

Let's say you want to create a select function, that returns the value of a property in an object:

You don't know beforehand what type of object will be passed to this function. However, you can set a constraint on key argument to ensure selecting only from existing properties on the object:

When you use this function with an object:

This is also useful when coding in IDE with auto-completion because it will allow you to select a seconds argument:

Utility Types#

Imagine you need to convert some type in a way to make every field optional.

TypeScript already has a Partial utility type. Utility types are generic types that perform some transformation on a given type-parameters.

In our case, Partial makes every key in a given object optional.

There are many other useful utility types described in the TypeScript documentation.

Sources#