Reusability of our code is one of the important things to try to implement when developing software. Therefore, it is difficult to keep all types consistent with different data. With Generics, we have the ability to write code that can adapt to a variety of types and we can control everything the way we need it.
Simple example
Suppose we have some kind of a secret value, that can be a string or a number or even an object or a boolean and we need to return it using a pure function. In TypeScript, without Generics we could write it like this:
// //using alias and union type TSecretValue = string | number | object | boolean; const secretValue = (id: TSecretValue): TUserId => id; //using only union const secretValue2 = (id: string | number | object | boolean ): string | number | object | boolean => id; //
Now, let’s take a look at this example using Generics.
// const editUserId3 = <T>(id: T): T => id; //fix for .tsx files const editUserId3 = <T,>(id: T): T => id; console.log(editUserId3('Hello')); // Good console.log(editUserId3(123)); // Good console.log(editUserId3(false)); // Good console.log(editUserId3({})); // Good //
One line of flexible code and full input and output type control. This line tells you: whatever type you provide, I will handle it and return it. And the keyword T is just a naming convention. Is it a boolean type? Then I will take boolean and return it. Is it a string type? No problem, I can handle whatever you give me as an argument for this function and return it to you with the same type.
The next example is with an object. Let’s say that we need to create user object with personal data, but that can be anything. An array of strings or for example a single number.
// type TUserName<T> = { id: number name: string personalData: T } type TUserName<T, T1> = { id: number name: string personalData: T, secretPersonalData: T1 } //
We just need to add our generic keyword to our type name and that’s it. If we need to handle more than one generic type parameter we can do so with a comma.

Here we have our user object. We are using a specified earlier type TUserName with one generic parameter. This one generic parameter will be an array of strings. But as I mentioned earlier it can be anything. Thanks to generics in our IDE we will have a full access to IntelliSense for any type we chose for our generic parameter. In this example, we have chosen an array so we have listed all possible array methods.
Generics with extends
With extends keyword we can force our generic parameter to have a specific property. Suppose we have a function that saves data. We have one generic parameter and it can be an object with any properties, but we want also this object to contain always age property. No matter what, an argument passed into saveObjectData function should always contain age property.
// const saveObjectData = <T extends { age: number }>(dataObject: T) => dataObject; const dataData = { name: 'Hero', age: 40 }; saveObjectData(dataData); //
Below one more example with Animal type that has one generic parameter and we extend it with a special batSpecies property.
// type IAnimal<T> = { name: string isExtinction: boolean dataProperties: T } const batAnimal: TAnimal<string> & { batSpecies: string }= { name: 'Bat', isExtinction: true, dataProperties: 'Lorem ipsum...', batSpecies: 'Vampire' } //
Example in real-life
Promises are a good way to show the advantages of using Generics. We can create a generic function, that will handle GET request with every type we provide. Then, when data is returned we have full control over all the properties of the provided earlier data type.
// type TUserData = { name: string age: number } // comma fix for .tsx files const httpGet = <T,>(endpoint: string): Promise<T> => { return fetch(url) .then((response) => { if (!response.ok) { return Promise.reject(); } return response.json(); }); } httpGet<TUserData[]>('/users').then((data) => { console.log(data); }); //
Naming convention
The most commonly used generic type parameter names are:
E – Element K – Key N – Number T – Type V – Value *for more than one generic type we should go with T1, T2, T3, etc.
Summary
When you first look at Generics in TypeScript it can be a little tricky to understand. But it is worth a try because it is a very powerful feature which allows us to write better and more reusable code.