How Do Generics Work In TypeScript?

Tim Mouskhelichvili
Tim Mouskhelichvili
5 minutes to read

Like many other programming languages, TypeScript gives developers access to generics. But what are they, and how and when to use them?

Generics make it simple to create reusable components. They allow developers to create a function, class, or interface that works with any data type.

In this article, I explain how generics work in TypeScript and show many code examples.

Let's get to it 😎.

typescript generics

The definition

Generics allow the creation of reusable components that work with any data type WITHOUT sacrificing type safety.

In TypeScript, generics work with:

Generics help developers:

  • To create reusable AND type-safe components.
  • To eliminate the need to cast types.
  • To implement generic algorithms (for example, when creating a queue).

Generic arguments are defined inside <>.

How to set one generic argument?

To set a generic argument, you must declare it within a component's definition.

Here is an example:

typescriptconst getValue = <P1,>(p1: P1): P1 => {
    return p1;
}

// string
const p1 = getValue('Tim');

// number
const p2 = getValue(27);

As you can see, this function accepts any data type as its argument AND returns the correct type using inference.

You can also set the generic type EXPLICITLY when calling a generic function:

typescriptconst getValue = <P1,>(p1: P1): P1 => {
    return p1;
}

// string
const p1 = getValue<string>('Tim');

// number
const p2 = getValue<number>(27);

Explicitly setting the generic type ensures you pass the correct argument type to the function.

How to set multiple generic arguments?

When using generics, a developer can choose to use more than one generic argument.

To set multiple generic arguments, you must declare them within the function, class, interface, or type definition.

Here is an example:

typescriptfunction getArgs<P1, P2, P3>(p1: P1, p2: P2, p3: P3): [ P1, P2, P3 ] { 
  return [
    p1,
    p2,
    p3
  ]
}

// [ string, string, number]
const p1 = getArgs('Tim', 'Mousk', 27);

// [ number, number, number]
const p2 = getArgs(20, 20, 30);

As you can see, this function accepts three parameters of any data type AND returns an array of parameters with their corresponding correct TypeScript types.

How to set a default type?

In TypeScript, generics support default types. A default type is a fallback that applies when a type is not specified AND cannot be inferred.

Declaring a default generic type is the same as declaring a default value for a function; you need to use the = operator.

Here is an example:

typescriptconst getValue = <P1 = string>(p1: P1): P1 => {
    return p1;
}

// string
const p1 = getValue('Tim');

In this example, the default type of the P1 generic type is string.

Generic constraints

So far, in all the examples, a generic argument could accept any data type. But what if we want to limit its allowed types? That's where constraints come into play.

A generic constraint allows a developer to restrict the allowed types for a generic argument.

To create a constraint, you must use the extends keyword.

Here is an example:

typescriptconst getValue = <P1 extends string | number>(p1: P1): P1 => {
    return p1;
}

// Works because the argument is of type string.
const p1 = getValue('Tim');

// Works because the argument is of type number.
const p2 = getValue(27);

// This doesn't work.
const p3 = getValue(true);

In this example, the allowed types for this function's parameter are string or number. The constraint was specified using a union type.

All other types WILL NOT WORK.

Generic functions

Creating a generic function makes it simple to reuse the same function but with different types.

Generics work with regular and arrow functions.

To make a generic function, a developer must set generic type(s) before the function parameters. The developer can then use those generic types as:

  • A parameter's type.
  • The return type of a function.

Here is an example of a generic function:

typescriptconst createPair = <P1, P2>(p1: P1, p2: P2): [P1, P2] => {
  return [ p1, p2 ];
}

// [ string, number ]
const p1 = createPair('Tim', 27);

// [ number, number ]
const p2 = createPair(50, 27);

In this example, the createPair function returns a pair of the first and second parameters with the CORRECT types.

Generic classes

Also, generics allow the creation of generalized classes that can support any data type in its properties, methods, or constructor.

To create a generic class, you must set one or more generic arguments in its definition.

Here is an example:

typescriptclass Pair<K, V> {
  public constructor(
    private key: K,
    private value: V
  ) { }

  public getKey () {
    return this.key;
  }

  public getValue () {
    return this.value;
  }
}

const pair = new Pair('Tim', 27);

// string
const key = pair.getKey();

// number
const value = pair.getValue();

In this example, we create a Pair class that accepts a key and value of any data type.

Because this class uses generics, when we use the getKey OR getValue functions, they return the CORRECT return types.

For getKey, its return type is the type of the key.

For getValue, its return type is the type of the value.

Example of a generic class: How To Create A Queue In TypeScript?

Generic interfaces

TypeScript interfaces also allow developers to use generics.

Here is an example:

typescriptinterface IAnimal<T> {
    name: string;
    data: T;
}

const p1: IAnimal<string> = {
    data: 'blablabla',
    name: 'Dog'
}

const p2: IAnimal<number> = {
    data: 10,
    name: 'Cat'
}

In this example, the IAnimal interface has two properties, name and data.

The data property is of a generic type, meaning we can use any data type we want. The following two constants demonstrate it.

In the first constant, data is of type string.

In the second constant, data is of type number.

Final thoughts

Although you will rarely use generics when coding in TypeScript, it is essential to understand this concept well. Here are a few reasons to do so.

First, to understand the type definitions of many TypeScript libraries (like React).

Second, to answer interview questions when you are looking for your next job.

And finally, to answer questions from your peers when they are stuck!

typescript generics

Here are some other TypeScript tutorials for you to enjoy:

Comments (0)
Reply to: