Table of contents
I used to think I knew Generics until I saw its application in a codebase and it dawned on me that my knowledge of it may be just recognition based than understanding.
I am writing this article to validate my knowledge of Generics and as well explain to beginners all that Generics is about.
What are Generics
Generics are simply placeholder data types.
So let's say we have an interface below
interface IUSer<T> {
age:T
}
Such that the age could be a string or a number such that when we want to use the age as a number or a string we tell typescript that T is the type of number or string.
const ageInString: IUser<string> ={ age: "twenty years" }
const ageInNumber: IUser<number> ={ age: 20 }
So with Generics we can create placeholder types and set them to what we want when want to use them.
The Microsoft typescript documentation defines generics as
Code templates that you can define and reuse throughout your codebase. They provide a way to tell functions, classes, or interfaces what type you want to use when you call them.
Generics are usually associated with angle brackets < > as their syntax.
Diving deeper
Let's say we have a function called findLeaders
such that it accepts a leader as params and returns a list of leaders.
In normal JS or "any" type of javascript our code will look this way
const findLeaders=(leader: any):any=>{
return leaders.filter(leader=>leader.isLeader)
}
We can solve the problem of any
and properly infer our types without so much repetition using generics.
interface ILeader {
isLeader: boolean;
// Other properties of the ILeader interface
}
//With functional
function findLeaders<T>(leaders: T[]): T[] {
return leaders;
}
findLeaders<ILeader>([{ isLeader: true }]);
However, for Arrow functions from my experience and implementation it is a different ball game to be able to properly make use of Generics in Arrow functions we have to extend what is inherited from the interface With this T copies all the types of ILeader.
//Extend does type Inheritance
const findLeaders = <T extends ILeader>(leaders: T[]): T[] => {
return leaders;
};
Multiple Generics
Generics in functions could be more than one, we could have <R, S, T>
as different place holders.
Let's say we have a function that adds the name, age and house address of a student to a single text/string in typescript.
export function addName<R, S, T>(name: R, age: S, address: T): string {
return `Hello my name is ${name} I am ${age} years old, I live at ${address}`;
}
addName<string, number, string>("Chibueze", 20, "Game"))
//OUTPUT: Hello my name is Chibueze I am 20 years old, I live at Game
We can also declare Generics with a class
class processName<T, U> {
private _age: T;
private _name: U;
constructor(age: T, name: U) {
this._age = age;
this._name = name;
}
getName() : T {
return this._age
}
}
let user = new processName<number, string>(100, 'Chibueze');
user.getName(); // Displays 'Chibueze'
Conclusion
The provided instance cited above is from my thinking and experimentaion. Numerous possibilities remain available when dealing with Generic types.
To illustrate:
You have the option to designate a default type
Infuse a degree of organization into your generic types to ensure that the types supplied during utilization possess specific attributes.
And numerous other possibilities.
The objective of this article was to introduce Generics and go in details as much I could and explain it properly for beginners to be able to grasp.
For more comprehensive insights, the TypeScript Generics Documentation delves into greater intricacies. It's certainly worth exploring that resource for further information.