Generics are usually considered most confusing part of Typescript. In this post I'll try to explain intuition behind generics and hopefully make them easier to use!
Common beginner confusions
(1) Generics will do some type interpretation at runtime
- No - Typescript is only available at compile time and not runtime!
(2) Generics map to input arguments
- No - Generic types can map to any input arguments as designed by developer.
const mapped = someFunction<T, U>(a, b)
// Type of a -> T and type of b -> U -- NO!
(3) Generics make my code look ugly 😖
- No magic bullet to solve this. However, if you leverage typescript's inference properly you can minimize this problem.
What problem generics solve
Let's say we have an identity function
const identityFunction = (t) => t
And we want:
- Function should accept any type of input
- Function's return type should match input's type
How can we type this function to achieve both objectives?
Naive solution
Statically type the input to be unknown
type. unknown
is top type in typescript's type system and every other type can be assigned to unknown
.
const identityFunction = (t: unknown) => t
However, this approach fails to achieve second objective that is type of function's return value is always unknown
regardless of the type of input.
let str = identityFunction('hello');
// ^? unknown
let num = identityFunction(785);
// ^? unknown
So ideally we want the type of function's input to be inferred when we use the function.
Typescript has such inference mechanism as we use it all the time:
let strBasic = "world";
// ^? string
let numBasic = 7856;
// ^? number
If there was a technique to leverage this Typescript's inference mechanism to infer the type of a function's input at usage we can solve our problem.
And that technique is Generic types 🎉
Generic types deploys typescript's inference mechanism to infer type of a function's input at usage 🔥
Generic definition and breakdown
So if we redefine the identity function using generic type it looks like:
const identityFunction = <T>(t: T) => t
Generic definition
Generic function will infer the type of input at usage. But we still need a way to reference that type in our function body while defining the function.
This type cannot be any static type such as any
or unknown
it needs to be dynamically inferred. Therefore, we need a special typescript variable T
to store this dynamic type.
Generic assignment and inference
Just like static type assignment
let v1: string // v1 is assigned type string
we use :
to assign type of input.
Additionally, in case of generics, type of input gets inferred at this step and is stored in variable T
.
Type inference only happens at the level of arguments within parentheses ()
and no where else in function body.
Image by Arek Socha from Pixabay
Top comments (0)