When working with types in TypeScript, we assume that we know what kind of type we will be working with.
For instance to define this log function:
const logAndReturn = (input: string): string => {
console.log(input);
return input;
};
In this simple function, we expect a string, return a string, and that's it.
But why should this be limited to just strings?
What if we try to pass a number
?
Hmm, that's a bit silly. We can't pass numbers to this function.
And it makes total sense.
One possible way of solving this would be to use any
as the type.
const logAndReturn = (input: any): any => {
console.log(input);
return input;
};
But using this makes it impossible to identify the type from outside.
It's basically as if you didn't use TypeScript at this point.
By the use of outside, I mean wherever we call this function, you should see what type it is being cast to like so:
So what then?
TypeScript generic type
This is precisely where generic types come in handy. They can be used to identify that specific called function as a type.
Making the function itself unaware of which type it's working with.
To identify a generic type, you must prefix the function with <Type>
where Type
is the generic variable.
Note: We often use
T
for generic types. However, it's not limited to any name.
Let's redo the above function as a generic typed function.
const logAndReturn = <T>(input: T): T => {
console.log(input);
return input;
};
Now, if we want to use this function and pass a string, we can do the following:
logAndReturn<string>('a string');
And on inspection, it states the following:
And if we want to convert this to our number, we can change the generic type used.
logAndReturn<number>(123);
As you can see, this is super powerful as we don't need to know the type upfront, but keep the reference to the correct types.
This method is not limited to these existing types. We can even define a custom interface that we want to use.
interface User {
firstname: string;
lastname: string;
}
logAndReturn<User>({firstname: 'Chris', lastname: 'Bongers'});
And in that case, our function will expect the User
type.
Conclusion
I hope you got an excellent first look at Generic types and how we can use them.
I'll go deeper into specific use-cases that will shed a broader light on them in the following articles.
Thank you for reading, and let's connect!
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter
Top comments (13)
One interesting feature missing in that description is that you can limit what types a generic type can be using
extends
; multiple types can be expressed via union.Definitely a big pro, I think in tomorrow's article I actually use multiple types 🙌.
Generics in general are such a blessing and great way to control types.
Java devs will find this feature very familiar :)
In C# the type passed in may be inferred. It that true here?
Generics can be inferred indeed.
If you define them like this for instance, in which case you can pass the inferred type you want.
But in general it will default to the T anyhow. this could just prove to be a security fallback.
This is help me to understand
Glad it helped Nazmul 💖
Indeed , Typescript generics are life saving.
This post was really helpful 👍
Thanks MirAli!
Oh nice! I'm just learning Typescript and I had no idea of generics. I would have defaulted to using "any".
Thanks for the tip!
Glad it helped!
Generics are so powerful and indeed a valid replacement of any in most cases (when used correctly).
They have way more power than described here, so they can solve a lot of issues 🤯
Awesome!
They definitely take some getting used to, and practive.