In this two-part series, we will take an in-depth look at TypeScript generics. This first post will give you a basic understanding of generics and how it works. In the later post, we’ll cover advanced inference and conditional types.
So, without further ado, let’s get started!
Why do we need generics?
Using generics allows us to write a typesafe code that will work with a wide range of primitives and objects.
Without generics, we would have to create a new type for every possible combination of data that we want to work with. With generics, we can write our function or method once and then reuse it for different types of inputs.
That sounds great, but what does TypeScript generics look like in practice? Let’s take a look at the syntax.
TypeScript uses angled bracket
<> and a type symbol
T to indicate generic syntax. In your client code, TypeScript will replace the type
T with the type you pass. This will make more sense if we take a look at an example:
The function in the code above takes a parameter of generic type
T. It then prints the parameter’s type to the console using the
In the first call, we pass in a string, and in the second one, we pass in an object. Since it’s generic, this function will work with any data and will execute successfully in both instances.
Classes and interfaces can also use generics:
Here we defined a generic class with a constructor and the
identifyType method. The type of argument that the
identifyType method accepts must match the
newSeed argument supplied to the constructor.
In other words, if you pass a string to the constructor,
identifyType would only accept strings as arguments.
Side note: You don’t have to use
T to indicate generics.
T is just a convention commonly used in TypeScript.
By default, TypeScript tries to infer types based on the arguments, but you can use explicit typecasting to force a specific type using the bracket syntax
In this example, the function
identifyType expects an argument of type string but receives a number. Because of this mismatch, TypeScript will produce an error.
Usually, we want to limit the type of
T to restrict the types our generic code accepts. We can do that using the
Say we want to limit the
identifyType function to accept only strings and numbers. Here’s how we would do it:
extends keyword, we can tell TypeScript which types our function or class should accept. In our case,
identifyType accepts only strings or numbers. When we attempt to pass a boolean, the function produces an error.
Using the type T
Generic code can only reference functions or properties of the objects that are common to any type of T. In other words, you can’t access anything specific to a particular type. You can only access methods and properties present in all of the generic types we specify.
For example, if we have a generic type that’s constrained to two types, we can only use functions and properties present in both of them:
In the example above, we would get an error if try to access the
birds property within our
generateAnimal function . However, we can access the
generate method without a problem since it’s present in both
You can construct a generic type out of another generic type. One way to do that is using the
keyof keyword is used to generate a new type based on the keys of another one.
We can use it to make sure we can only specify keys that are present in our generic object, like so:
Here we use the
keyof keyword to create a new type and restrict the possible values for the second argument of the
getPropertyValue function. Now it only accepts strings that match the keys present in the
In this article, we covered the basics of TypeScript generics. We looked at using generics to constrain the types our code accepts and explored some of the features available when working with generic objects.
If you’d like to get more web development, React and TypeScript tips consider
following me on Twitter,
where I share things as I learn them.