Typescript Generics: Strong Yet Flexible Types

Rick Moore
Nerd For Tech
Published in
4 min readAug 30, 2021

--

Photo by Universal Eye on Unsplash

Typescript is a powerful extension of Javascript, that allows us to take advantage of strongly typed variables, and comes with some useful tools to create flexible, reusable code, that can still be type checked for errors, even if the types might vary between usages.

I’ll be diving into generics in Typescript. These let us tie variables together, regardless of their initial types. I’ll assume that you already know the basics of Typescript. If not, check out my earlier blog post.

Basic Usage

Let us consider a few versions of a very basic function. It takes in an argument of an unknown type, and returns whatever is assigned to that argument variable. In JS, we would simply declare the function:

Typescript actually isn’t happy with this, we are not declaring any variable type at all, so we get an error:

Normal Function Error

The compiler would like to know what kind of argument to expect to be passed into this function, and since we don’t know, we may be inclined to add the any type declaration to simply allow anything we’d like to pass in.

Okay, now the compiler is happy, but we’ve really not accomplished much. We aren’t utilizing the type checks that Typescript brings us, and simply created a workaround. If our goal is to ensure that if a string type is passed in, then a string will be returned, and if a number type is passed in, then a number is returned, we may have to write 2 different type safe functions:

Now we can use the respective function to get our desired outcome, however, this doesn’t look very DRY if you ask me. The logic of the function is repeated, and we need to repeat our code to accommodate our possible types of arguments. Here’s where generics come in to save the day. Generics use type parameters to declare ‘generic’ types, that can be used across the function, but ensure that the same type is used in different parts of the code. Here is our function, with the type declared as the generic T or <T>.

This code creates a link between the argument passed in and the type of the variable returned from the function. What’s passed in must be the same type as what is returned.

Now we can reuse the function, and it will work correctly with a number or a string as an argument. What if we tried to force our logic to return a string no matter what?

Casting Error

This error makes sense. If we are casting our argument into a string in the logic of the function, then we can’t maintain that certainty of the T generic type. If we passed in a number, it would be impossible for this function to also return a number, because it would be cast in to a string. Typescript thinks ahead and let’s you know this will be an issue before it becomes one.

Using generics with multiple types

We can use the same type parameter syntax to declare multiple types to be referenced through the function.

This will return false, a boolean type set with the V generic type.

Creating classes with generic types

We can also create classes that utilize these flexible generic types. Let’s create a custom array class that can be used to create an array of whatever type we decide later.

We can use the T type declaration in subsequent class functions to ensure that the types will remain consistent.

Using generics to extend existing type definitions

We may run into a situation where we have an existing interface or type definition, and we’d like to use that, but add new generic definitions in addition to it. we can use the extends keyword to achieve this.

We can try to pass in just one of the expected types from the funcArgs interface, but we get an error.

funcArgs Error

This makes sense, we’re not passing in the proper argument types.

funcArgs Error 2

Even if we pass in the single num argument properly, we still haven’t met the requirements set by the funcArgs interface, which needs a num and a str variable.

There we go. We’ve satisfied the constraints of the interface, but adding an admin argument as a boolean, will set the T generic type to the funcArgs type plus a boolean, which can be referenced all together in the function logic. This would normally throw an error, but since we used the T extends keyword, the compiler is happy.

Conclusion

Generics are common in other strongly typed languages, and a full understanding of their usage opens a lot of possibilities for developers to create reusable code that still benefits from strong type checking. Thank you for reading and happy coding!

--

--

Rick Moore
Nerd For Tech

Software Engineer at Thrive TRM. Specializing in Ruby on Rails, React/Redux and JavaScript.