Advanced Typescript Series: Generics
Ritik Banger
Posted on August 4, 2022
As we all know, the power of Typescript and how typescript leverage the power of types to make the codebase robust. I am coming up with a full-fledged series on advanced typescript topics.
I would be covering Generics, Intersection types, Union types, Utility types, Infer types, conditional types, mapped types, template literal types as well as type guards in this series.
Today, I am covering the generics in typescript.
The development in today's world is focused on component reusability so that not only today but the component can be reused tomorrow too. The word 'reusable' itself is self-explanatory, and it makes our codebase flexible.
But there is a problem that it is not easy to write reusable code, and we simply write code while focusing on a single kind of data or type and later when some new data or type come into picture, we need to write another component rather than using the previous one.
One another scenario a developer working with typescript and some package for example, say, react hook form faces when he writes a reusable component like, common input component, but he is unable to give type to the register or the control function of the react hook form and typescript infer somethng by itself and the developer will face errors at the end. Here generics can help out. Depending on your field values, you can pass the interface to generic component, and it would do the rest.
Now, let us understand how generics can help us to solve such problems.
Generics is the way which allow us to write such flexible code that can work with different data types rather than a single data type. This enforces code reusability and avoid duplication of the code. It also adds an extra layer of abstraction.
Generics is a fully supported feature of typescript. With generics, one can pass types as parameters to another type, function, class, or interface.
Generics in TypeScript code can be written in angle brackets <>, in the format , where T represents a passed-in type. can be read as a generic of type T. The T is also known as the type parameters.
Let us start with a very basic code to understand this.
We have a function here that takes a string argument and returns a string.
function returnString(arg: string): string {
return arg;
}
Another such function doing the same kind of task for number can be,
function returnNumber(arg: number): number {
return arg;
}
Now, we can create a common function for both the use cases with the help of generics.
function returnGeneric<Type>(arg: Type): Type {
return arg;
}
const output = returnGeneric<string>("some string goes here");
Pretty simple right, now let us take another example for API Call.
type Students = {
name: string;
section: string;
}
type Faculty = {
name: string;
subject: string;
}
async function genericFetchApi<T>(path: string): Promise<T> {
const response = await fetch(`https://yourdomain.com/api${path}`);
return response.json();
}
const studentsData = await fetchApi<Students[]>('/students')
const facultyData = await fetchApi<Faculty[]>('/faculty')
export {}
Now, this is how we can consume a common function with different type of arguments and different return type for different use cases.
On moving further, there is Something in generics that you should have an idea of. It is generic type constraint. Using constraints is basically to put restrictions. JavaScript revolves around objects, and some scenario exists where you expect something and receive something else from the backend. In such cases or other, these constraints are helpful. Only specific types must be permitted to be given into the generic via a generic type argument. You can impose restrictions on your parameter to offer an extra level of precision and narrowing the types to your generic.
You can use something like .
'extends Record' is known as generic type constraint. It enables you to define that the type that follows the extends keyword must be assignable to your generic type. Record in this context denotes an object with strings as the key type and any as the value type. You can replace the 'any' with the value type of your object. Also, Any valid TypeScript type may be extended by your type parameter.
Usage:
const genericFunc = <T extends number>(x: T) => x;
const stringTest = genericFunc('a'); // Argument of type 'string' is not assignable to parameter of type 'number'
const numberTest = genericFunc(99); //passes
End Note: Generics will make your codebase robust, and they really help in some way or other. You can anytime start using generics. Let me know in the discussions if you like this article or not, also, if you have any suggestions, do let me know.
I would be coming out with the next learning in this series very soon. Stay tuned and follow me for more updates.
Posted on August 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.