Generics in Typescript and how to use them
Sometimes I wonder whether I love or hate Typescript. I see the benefits of implementing it in the majority of frontend solutions, but I struggle with the syntax and the mumbo jumbo names that don't tell me anything. "Generics" would be one of those, as the name kindly suggests too, it's a name that could mean anything 🤷 When I find myself in the situation where I think I have grasped something but I haven't quite, the best solution is to try to write something about it to bring in clarity!
So what are Generics?
Generics in TypeScript provide a way to create reusable data structures (arrays, stacks, queues, linked lists etc.), components and functions that can work with different data types.
Take for example:
With generics you can create a container type that can hold different types of values based on needs, e.g.:
This is useful when you want to create a flexible and type-safe container, such as a dictionary or a set. You'll be guaranteed, whether the structure will accommodate strings or numbers, that it will follow the interface
Dictionary and expect an object with keys whose value will reflect the data you use it with.
Let's make another practical example; let's say you want to have a function that allows you to print whatever type of array in a particular format, like an ordered list. In this case, the type of the data passed is not relevant to you, you only care that it's presented in an ordered list.
Now you can use this function to format the arrays before appending them to a div:
In the example,
printArray doesn't care whether the array inputted is a numeric or a string one, but it makes sure to guarantee type safety based on the data type you pass to it.
Not bad, right?
What is a somewhat more practical application of generics in the real-life coding world?
Generics can be useful when working with promises and asynchronous operations, allowing you to specify the type of the resolved value.
While this example is not perfect, it offers us a great way to dynamically query for various URLs, and for each case we have the possibility to provide the type we expect to receive: we query for
https://api.example.com/users/1 - we expect to receive a response containing user information - so we provide the interface
UserData to the function
fetchData<UserData>(apiUrl) and if the request doesn't fail, our response will be already using the correct interface. Neat!
Challenges with Generics
Not all that shines is gold, and the same can be said about generics. While they offer this great versatility, you can imagine they also provide a lot of complexity to a codebase, as they will require developers to pay better attention to the data flow and make sure that things won't go as unexpected.
Together with the broader learning curve proposed to developers, there are also various limitations of type inferring that might make generics limiting to a practical application. No idea about those limits, will let you know when I stumble on those :D
Long story short
If you use them, make sure it makes sense on why you're using them and that they actually bring benefits on what you're doing. If you're working on a npm package that needs to accommodate different solutions (idk, a table with columns that can sort according to the data type provided, where you might not know ahead of time what values will be entered), generics might be very well what you're looking for! Bear in mind however, that they will always create an extra layer of complexity that you need to account for your code and for all your team mates!